16

助けてください!

背景情報

SQL Server 2005 データベースにアクセスする WPF アプリケーションがあります。データベースは、アプリケーションが実行されているマシン上でローカルに実行されています。

Linq DataContext を使用するあらゆる場所で using { } ステートメントを使用し、DataContext コンストラクターに戻る前に、開かれ、それを使用して SqlCommand が実行された SqlConnection オブジェクトを返す関数の結果を渡します。

// In the application code
using (DataContext db = new DataContext(GetConnection()))
{
    ... Code 
}

getConnection は次のようになります (読みやすくするために関数から「綿毛」を取り除きましたが、不足している追加機能はありません)。

// Function which gets an opened connection which is given back to the DataContext constructor
public static System.Data.SqlClient.SqlConnection GetConnection()
{
   System.Data.SqlClient.SqlConnection Conn = new System.Data.SqlClient.SqlConnection(/* The connection string */);

    if ( Conn != null )
    {
        try
        {
            Conn.Open();
        }
        catch (System.Data.SqlClient.SqlException SDSCSEx)
        {
             /* Error Handling */
        }

        using (System.Data.SqlClient.SqlCommand SetCmd = new System.Data.SqlClient.SqlCommand())
        {
            SetCmd.Connection = Conn;
            SetCmd.CommandType = System.Data.CommandType.Text;

            string CurrentUserID = System.String.Empty;
            SetCmd.CommandText = "DECLARE @B VARBINARY(36); SET @B = CAST('" + CurrentUserID + "' AS VARBINARY(36)); SET CONTEXT_INFO @B";

            try
            {
                SetCmd.ExecuteNonQuery();
            }
            catch (System.Exception)
            {
                /* Error Handling */
            }
        }

        return Conn;
    }

アプリケーションが WPF であることが、私が抱えている問題に関係しているとは思いません。

私が抱えている問題

Sql Server Management studio で SqlConnection が DataContext と共に破棄されているにもかかわらず、次のように開いている接続の負荷をまだ見ることができます。

status : 'Sleeping' 
command : 'AWAITING COMMAND' 
last SQL Transact Command Batch : DECLARE @B VARBINARY(36); SET @B = CAST('GUID' AS VARBINARY(36)); SET CONTEXT_INFO @B

最終的に接続プールが使い果たされ、アプリケーションは続行できなくなります。

したがって、どういうわけか SQLCommand を実行して Context_Info を設定することは、DataContext が破棄されたときに接続が破棄されないことを意味しているとしか言えません。

接続が使用されている DataContext が破棄されたときに、接続が閉じられて破棄されるのを止める明らかなことを誰でも見つけることができますか?

4

6 に答える 6

19

MSDNから( DataContext Constructor (IDbConnection)):

開いている接続を提供すると、DataContext はそれを閉じません。したがって、正当な理由がない限り、開いている接続で DataContext をインスタンス化しないでください。

したがって、基本的に、接続が解放される前に、GC がファイナライズするのを待っているようです。これを行うコードがたくさんある場合、1 つの方法Dispose()として、データ コンテキストの部分クラスをオーバーライドし、接続を閉じることができます。ただし、データ コンテキストが接続の所有権を前提としていることを必ず文書化してください。

    protected override void Dispose(bool disposing)
    {
        if(disposing && this.Connection != null && this.Connection.State == ConnectionState.Open)
        {
            this.Connection.Close();
            this.Connection.Dispose();
        }
        base.Dispose(disposing);
    }

個人的には、接続を「使用」している限り(複数の操作を実行できるようにする)、開いている接続(通常のデータコンテキスト、上記のハックなし)を喜んで提供します-つまり、

using(var conn = GetConnection())
{
   // snip: some stuff involving conn

   using(var ctx = new FooContext(conn))
   {
       // snip: some stuff involving ctx
   }

   // snip: some more stuff involving conn
}
于 2008-11-06T15:01:45.020 に答える
7

SqlProviderLINQ で使用される は、 SQL 接続を開く側である場合にDataContextのみ ( を介して) SQL 接続を閉じます。SqlConnectionManager.DisposeConnection既に開いているSqlConnectionオブジェクトをDataContextコンストラクターに渡すと、閉じられません。したがって、次のように書く必要があります。

using (SqlConnection conn = GetConnection())
using (DataContext db = new DataContext(conn))
{
    ... Code 
}
于 2008-11-06T15:02:17.047 に答える
3

Entity Framework を使用して同じ問題が発生しました。私はブロックObjectContextに巻き付いていました。using

を呼び出したときに接続が確立されましたSaveChanges()が、ステートメントが範囲外になった後、 SQL Management Studioに .NET SQL クライアント用の がusing残っていることに気付きました。"AWAITING COMMAND"これは、デフォルトで接続プールが有効になっている ADO.NET プロバイダーの動作に関係しているようです。

MSDNの「Using Connection Pooling with SQL Server」から(強調は鉱山):

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

またClearAllPoolsClearPool必要に応じて、プールされたすべての接続を明示的に閉じると便利なようです。

于 2009-03-31T06:45:26.200 に答える
1

接続は参照されなくなりましたが、GC が完全に破棄するのを待っていると思います。

解決:

自動生成されたものから派生する独自の DataContext クラスを作成します。(ベースの名前を変更して、他のコードを変更する必要がないようにします)。

派生した DataContext で、Dispose() 関数を追加します。その中で - 内部接続を破棄します。

于 2008-11-06T15:02:41.133 に答える
1

助けてくれてありがとう、それは今解決されました..

基本的に、上記のほとんどの回答の要素を取り、上記のように DataContext コンストラクターを実装しました (既にコンストラクターをオーバーロードしていたので、大きな変更はありませんでした)。

// Variable for storing the connection passed to the constructor
private System.Data.SqlClient.SqlConnection _Connection;

public DataContext(System.Data.SqlClient.SqlConnection Connection) : base(Connection)
{
    // Only set the reference if the connection is Valid and Open during construction
    if (Connection != null)
    {
        if (Connection.State == System.Data.ConnectionState.Open)
        {
            _Connection = Connection;                    
        }
    }           
}

protected override void Dispose(bool disposing)
{        
    // Only try closing the connection if it was opened during construction    
    if (_Connection!= null)
    {
        _Connection.Close();
        _Connection.Dispose();
    }

    base.Dispose(disposing);
}

上記のいくつかの提案ではなく、これを行う理由はthis.Connection、dispose メソッドでアクセスするとObjectDisposedExceptionがスローされるためです。

そして、上記は私が望んでいたようにうまくいきます!

于 2008-11-06T15:34:26.390 に答える
0

MSDNが指摘しているDisposeように、接続を閉じる必要があります。

SqlConnection が範囲外になると、閉じられません。したがって、Close または Dispose を呼び出して、接続を明示的に閉じる必要があります。Close と Dispose は機能的に同等です。接続プールの値 Pooling が true または yes に設定されている場合、基礎となる接続は接続プールに戻されます。一方、Pooling が false または no に設定されている場合、サーバーへの基になる接続は閉じられます。

私の推測では、あなたの問題はGetContext().

于 2008-11-06T15:02:03.490 に答える