そもそもコメントだけで申し訳ありませんが、ADO.NETの機能をDB-Classにカプセル化するのは賢明だと多くの人が思っているので、ほぼ毎日同じようなコメントを投稿しています(私も10年前です)。ほとんどの場合、アクション用の新しいオブジェクトを作成するよりも高速に見えるため、静的/共有オブジェクトを使用することにしました。
これは、パフォーマンスの観点からも、フェイルセーフの観点からも良い考えではありません。
接続プールの領域を密猟しないでください
ADO.NETがADO-NET Connection-Pool内のDBMSへの基になる接続を内部的に管理するのには十分な理由があります。
実際には、ほとんどのアプリケーションは、接続に1つまたはいくつかの異なる構成のみを使用します。これは、アプリケーションの実行中に、多くの同一の接続が繰り返し開かれたり閉じられたりすることを意味します。接続を開くコストを最小限に抑えるために、ADO.NETは接続プールと呼ばれる最適化手法を使用しています。
接続プールにより、新しい接続を開かなければならない回数が減ります。プーラーは、物理接続の所有権を維持します。所定の接続構成ごとにアクティブな接続のセットを維持することにより、接続を管理します。ユーザーが接続に対してOpenを呼び出すと、プーラーはプール内で使用可能な接続を探します。プールされた接続が利用可能な場合、新しい接続を開くのではなく、呼び出し元に返します。アプリケーションが接続でCloseを呼び出すと、プーラーは接続を閉じるのではなく、アクティブな接続のプールされたセットに戻します。接続がプールに戻されると、次のOpen呼び出しで再利用できるようになります。
したがって、実際には接続は作成、オープン、クローズされないため、接続の作成、オープン、クローズを回避する理由はありません。これは、接続を再利用できるかどうかを接続プールが知るための「唯一の」フラグです。しかし、これは非常に重要なフラグです。接続が「使用中」の場合(接続プールが想定)、新しい物理接続は、DBMSへのオープンエンドでなければならず、非常に高価です。
つまり、パフォーマンスは向上しませんが、逆になります。指定された最大プールサイズ(デフォルトは100)に到達すると、例外が発生します(開いている接続が多すぎる...)。したがって、これはパフォーマンスに多大な影響を与えるだけでなく、厄介なエラーや(トランザクションを使用せずに)データダンプ領域の原因にもなります。
静的接続を使用している場合でも、このオブジェクトにアクセスしようとするすべてのスレッドに対してロックを作成しています。ASP.NETは本質的にマルチスレッド環境です。そのため、これらのロックが発生する可能性が高く、パフォーマンスの問題が発生します。実際には遅かれ早かれ、さまざまな例外が発生します(ExecuteReaderには、オープンで利用可能なConnectionが必要です)。
結論:
- 接続やADO.NETオブジェクトを再利用しないでください。
- それらを静的/共有にしないでください(VB.NET内)
- 常に作成、オープン(接続の場合)、使用、クローズ、および必要な場所への配置(メソッドのfe)
- を使用して、
using-statement
暗黙的に破棄およびクローズします(接続の場合)。
これは、Connectionsだけではありません(もっとも注目に値します)。実装IDisposable
するすべてのオブジェクトを破棄する必要があります(最も単純な方法using-statement
)System.Data.SqlClient
。
上記のすべては、すべてのオブジェクトをカプセル化して再利用するカスタムDBクラスに反しています。それが私がそれを捨てるようにコメントした理由です。それは問題の原因にすぎません。
編集:-メソッドの可能な実装はretrievePromotion
次のとおりです:
public Promotion retrievePromotion(int promotionID)
{
Promotion promo = null;
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["MainConnStr"].ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
var queryString = "SELECT PromotionID, PromotionTitle, PromotionURL FROM Promotion WHERE PromotionID=@PromotionID";
using (var da = new SqlDataAdapter(queryString, connection))
{
// you could also use a SqlDataReader instead
// note that a DataTable does not need to be disposed since it does not implement IDisposable
var tblPromotion = new DataTable();
// avoid SQL-Injection
da.SelectCommand.Parameters.Add("@PromotionID", SqlDbType.Int);
da.SelectCommand.Parameters["@PromotionID"].Value = promotionID;
try
{
connection.Open(); // not necessarily needed in this case because DataAdapter.Fill does it otherwise
da.Fill(tblPromotion);
if (tblPromotion.Rows.Count != 0)
{
var promoRow = tblPromotion.Rows[0];
promo = new Promotion()
{
promotionID = promotionID,
promotionTitle = promoRow.Field<String>("PromotionTitle"),
promotionUrl = promoRow.Field<String>("PromotionURL")
};
}
}
catch (Exception ex)
{
// log this exception or throw it up the StackTrace
// we do not need a finally-block to close the connection since it will be closed implicitely in an using-statement
throw;
}
}
}
return promo;
}