5

静的クラスの次のメソッドでは、接続プールが最大になっているため、タイムアウト例外が発生します。

デバッグモードでSQLManagementStudioを調べたところ、150のスリーププロセスがあることがわかりました。

接続が自動的に閉じられることを期待していました...静的メンバーとして配置しようとしましたが、それでも同じエラーが発生しました。

何か案は?ここにコードがあります:

public static Decimal ExecuteScalarDec(string procName, params object[] parameters)
{
    try
    {
        return (Decimal)DatabaseFactory.CreateDatabase().ExecuteScalar(procName, parameters);
    }
    catch (Exception ex)
    {
        throw new Exception(procName.ToString() + " " + parameters.ToString(), ex);
    }
}

「設計上、ほとんどのデータベースクラスメソッドは、呼び出しごとにデータベースへの接続の開始と終了を処理します。したがって、アプリケーションコードには、接続を管理するためのコードを含める必要はありません。」ExecuteReaderは例外です(リソースを返すため)。ExecuteScalarは不安定です:「スカラー」を返します。ただし、スカラーはかなり重い可能性があると思います。大きなデータ型の戻り値から構築されたストリームであり、接続を開いたままにする必要があります。– Remus Rusanu

ユーザーを登録した後、「コメントには50の評判が必要です」と表示されているため、コメントできませんでした...

executeScalar()で列IDを返し、値が返されます-スカラーを実行する次の呼び出しは、値を受け取った後にのみ呼び出されるため、これを知っています...ストリームが永久に開いたままにしますそして、SQL Managementで、すべてのプロセスがスリープしていることを確認しました。

4

1 に答える 1

6
public static Decimal ExecuteScalarDec(string procName, params object[] parameters)
{
    try
    {
        using (Database database = DatabaseFactory.CreateDatabase())
        {
            return (Decimal)database.ExecuteScalar(procName, parameters);
        }
    }
    catch (Exception ex)
    {
        throw new Exception(procName.ToString() + " " + parameters.ToString(), ex);
    }
}

アップデート

OK、これはEnterpriseLibraryコードなので。Databaseクラスは次のようにExecuetScalarを実装します(他の署名は最終的にこれに崩壊します):

 public virtual object ExecuteScalar(DbCommand command)
        {
            if (command == null) throw new ArgumentNullException("command");

            using (ConnectionWrapper wrapper = GetOpenConnection())
            {
                PrepareCommand(command, wrapper.Connection);
                return DoExecuteScalar(command);
            }
        }

そして、ConnectionWrapperは接続(リンク内のソースファイルの終わり)を破棄するので、理論は進みます。呼び出しOKであり、接続を破棄する必要があります。

GetOpenConnection()メソッドは、接続を破棄するラッパーを返します...現在のTransactionScopeConnections:に存在する場合を除きます。

 protected ConnectionWrapper GetOpenConnection(bool disposeInnerConnection)
    {
        DbConnection connection = TransactionScopeConnections.GetConnection(this);
        if (connection != null)
        {
            return new ConnectionWrapper(connection, false);
        }

        return new ConnectionWrapper(GetNewOpenConnection(), disposeInnerConnection);
    }

そしてTransactionScopeConnections、これが接続を返す方法です:

  public static DbConnection GetConnection(Database db)
    {
        Transaction currentTransaction = Transaction.Current;

        if (currentTransaction == null)
            return null;

        Dictionary<string, DbConnection> connectionList;
        DbConnection connection;

        lock (transactionConnections)
        {
            if (!transactionConnections.TryGetValue(currentTransaction, out connectionList))
            {
                // We don't have a list for this transaction, so create a new one
                connectionList = new Dictionary<string, DbConnection>();
                transactionConnections.Add(currentTransaction, connectionList);

                // We need to know when this previously unknown transaction is completed too
                currentTransaction.TransactionCompleted += OnTransactionCompleted;
            }
        }

        lock (connectionList)
        {
            // Next we'll see if there is already a connection. If not, we'll create a new connection and add it
            // to the transaction's list of connections.
            // This collection should only be modified by the thread where the transaction scope was created
            // while the transaction scope is active.
            // However there's no documentation to confirm this, so we err on the safe side and lock.
            if (!connectionList.TryGetValue(db.ConnectionString, out connection))
            {
                // we're betting the cost of acquiring a new finer-grained lock is less than 
                // that of opening a new connection, and besides this allows threads to work in parallel
                connection = db.GetNewOpenConnection();
                connectionList.Add(db.ConnectionString, connection);
            }
        }

        return connection;
    }

これで、私が間違っていない限り、TransactionsScopeConnectionsは常に新しいデータベースオブジェクトの新しい接続を作成し(あなたの場合のように)、それらを内部ディクショナリに保持します。DatabaseオブジェクトはDisposableを実装していないため、このTransactionScopeConnecitons内部リストから接続をクリーンアップするのは誰であるかを正確に判断できません。

マット、 CLRリークに関するこの記事の手順に従って、プロセスに多数のデータベースオブジェクトがあるかどうかを確認することは可能ですか?SOSをロードし、を実行し!dumpheap -type Microsoft.Practices.EnterpriseLibrary.Data.Databaseます。多くのオブジェクトを見つけた場合、それらのいくつかのピンスタックをトレースできますか?!gcroot <AddressOfObject>

于 2009-07-23T06:29:02.267 に答える