sql >> データベース >  >> RDS >> Sqlserver

ExecuteReaderには、開いていて利用可能な接続が必要です。接続の現在の状態は接続中です

    そもそもコメントだけで申し訳ありませんが、ADO.NETの機能をDBクラスにカプセル化するのが賢明だと多くの人が考えているので(私も10年前)、ほぼ毎日同様のコメントを投稿しています。ほとんどの場合、アクション用に新しいオブジェクトを作成するよりも高速であると思われるため、静的/共有オブジェクトを使用することにします。

    これは、パフォーマンスの観点からも、フェイルセーフティの観点からも良い考えではありません。

    接続プールの領域を密かにしないでください

    ADO.NETがADO-NET接続プールでDBMSへの基盤となる接続を内部的に管理するのには十分な理由があります:

    実際には、ほとんどのアプリケーションは、接続に1つまたはいくつかの異なる構成のみを使用します。これは、アプリケーションの実行中に、多くの同一の接続が繰り返し開かれたり閉じられたりすることを意味します。接続を開くコストを最小限に抑えるために、ADO.NETは接続プールと呼ばれる最適化手法を使用します。

    接続プーリングにより、新しい接続を開く必要がある回数が減ります。プール担当者は、物理接続の所有権を維持します。指定された接続構成ごとに一連のアクティブな接続を維持することにより、接続を管理します。ユーザーが接続でOpenを呼び出すたびに、プール担当者はプールで使用可能な接続を探します。プールされた接続が使用可能な場合、新しい接続を開く代わりに、それを呼び出し元に返します。アプリケーションが接続でCloseを呼び出すと、プール担当者は接続を閉じるのではなく、プールされたアクティブな接続のセットに返します。接続がプールに戻されると、次のOpen呼び出しで再利用できるようになります。

    したがって、実際には接続が作成、開封、および閉じられていないため、接続の作成、開封、または閉路を回避する理由は明らかにありません。これは、接続を再利用できるかどうかを知るための接続プールの「唯一の」フラグです。ただし、これは非常に重要なフラグです。接続が「使用中」の場合(接続プールが想定)、新しい物理接続をDBMSに対してオープンエンドにする必要があり、非常にコストがかかります。

    したがって、パフォーマンスは向上しませんが、その逆になります。指定された最大プールサイズ(デフォルトは100)に達すると、例外が発生することもあります(開いている接続が多すぎます...)。したがって、これはパフォーマンスに多大な影響を与えるだけでなく、厄介なエラーや(トランザクションを使用しない場合の)データダンプ領域の原因にもなります。

    静的接続を使用している場合でも、このオブジェクトにアクセスしようとするすべてのスレッドに対してロックを作成しています。 ASP.NETは、本質的にマルチスレッド環境です。したがって、これらのロックが発生する可能性が高く、せいぜいパフォーマンスの問題が発生します。実際には遅かれ早かれ、さまざまな例外が発生します(たとえば、ExecuteReaderには開いた利用可能な接続が必要です

    結論

    • 接続やADO.NETオブジェクトをまったく再利用しないでください。
    • 静的/共有(VB.NETの場合)にしないでください
    • 常に作成、オープン(Connectionsの場合)、使用、クローズ、および必要な場所(メソッド内など)で破棄します
    • using-statementを使用します 暗黙的に破棄して閉じる(接続の場合)

    これは、接続だけでなく(最も注目に値するものですが)当てはまります。 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 [email protected]";
            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;
    }
    


    1. これらの4つの一般的なDBAの間違いを避けてください

    2. ORA-04021:オブジェクトのロック待機中にタイムアウトが発生しました

    3. SQLiteテーブルの作成

    4. PostgreSQLクエリキャッシングと負荷分散の概要