C#DataBase.ExecuteScalar中的连接泄漏

Mat*_*att 4 c# sql database enterprise-library executescalar

静态类中的以下方法给出了超时异常,因为连接池被最大化.

在调试模式下,我查看了sql Management studio,看到有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);
    }
}
Run Code Online (Sandbox Code Playgroud)

"按照设计,大多数Database类方法在每次调用时都会处理数据库连接的打开和关闭.因此,应用程序代码不需要包含用于管理连接的代码." ExecuteReader是一个例外(因为它返回一个资源).ExecuteScalar处于不确定状态:它返回一个'标量'.但是,我猜标量可能很重,例如.从大型数据类型返回构造的流,这将需要保持打开的连接. - Remus Rusanu

我无法评论你的答案,因为它说"评论需要50个声誉"我注册了我的用户后......

我在executeScalar()中返回一个列Id并且返回了值 - 我知道这是因为下一次执行标量的调用仅在我收到一个值后被调用...它不会理解为流将是永远保持开放我在sql Management中看到所有进程都在休眠.

Rem*_*anu 5

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

更新

好的,因为这是EnterpriseLibrary代码.该数据库类实现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);
            }
        }
Run Code Online (Sandbox Code Playgroud)

并且ConnectionWrapper处理连接(链接中的源文件的结尾),理论上,你的调用应该没问题并且处理连接.

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);
    }
Run Code Online (Sandbox Code Playgroud)

以下是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;
    }
Run Code Online (Sandbox Code Playgroud)

现在除非我弄错了,否则TransactionsScopeConnections它将始终为一个全新的数据库对象(如你的情况)创建一个新的联系,并将它们保存在内部字典中.Database对象没有实现Disposable,所以我在确定应该从这个TransactionScopeConnecitons内部列表中清除连接的确切位置时丢失了.

Matt,是否可以按照本文中有关CLR泄漏的步骤进行操作,并查看流程中是否有大量的Database对象?加载SOS并执行操作!dumpheap -type Microsoft.Practices.EnterpriseLibrary.Data.Database.如果你发现很多物体,你可以跟踪其中一些物体上的引脚堆栈!gcroot <AddressOfObject>

  • 这不起作用,因为数据库不是Disposable (2认同)