【问题标题】:Connection Leak in C# DataBase.ExecuteScalarC# DataBase.ExecuteScalar 中的连接泄漏
【发布时间】:2009-07-23 06:23:46
【问题描述】:

静态类中的以下方法给了我一个超时异常,因为连接池已满。

在调试模式下,我查看了 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);
    }
}

“根据设计,大多数 Database 类方法在每次调用时处理与数据库的连接的打开和关闭。因此,应用程序代码不需要包含用于管理连接的代码。”。 ExecuteReader 是一个例外(因为它返回一个资源)。 ExecuteScalar 处于不确定状态:它返回一个“标量”。但是,我猜标量可能非常重,例如。从大型数据类型返回构造的 Stream,这需要保持连接打开。 ——莱姆斯·鲁萨努

我无法对您的回答发表评论,因为它说“评论需要 50 声望”在我注册用户后...

我在 executeScalar() 中返回一个列 ID 并返回该值 - 我知道这一点,因为仅在我收到一个值后才调用执行标量的下一个调用... 溪流将永远保持开放是没有意义的 而且我在sql管理中看到所有进程都在休眠。

【问题讨论】:

  • ExecuteScalar() 方法对底层 DbConnection 对象有什么作用——它是调用 Dispose() 还是 Close(),例如,通过使用“using”语句?

标签: c# sql database enterprise-library executescalar


【解决方案1】:
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);
    }
}

更新

好的,因为这是 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 处理连接(链接中源文件的结尾)所以,理论上,您的调用应该可以并处理连接。

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 内部列表中的连接。

Matt,是否可以按照this article about CLR leaks 中的步骤查看您的进程中是否存在大量数据库对象?加载 SOS 并执行 !dumpheap -type Microsoft.Practices.EnterpriseLibrary.Data.Database。如果你发现很多对象,你能用!gcroot &lt;AddressOfObject&gt;追踪其中一些的pin stack吗

【讨论】:

  • 那么您最好将其设为一次性,否则您将从现在到后天跟踪泄漏的连接。
  • 那是你的 Database 类的错:如果它不是一次性的,那么它应该确保 在它的 ExecuteScalar 方法中处理连接。
  • @Remus:如果它是一次性类,它不需要是 Disposable...它可以使 ExecuteScalar 打开连接、执行和关闭.听起来它并没有这样做:(基本上,如果 Database 类将数据库连接作为状态引用,它应该实现 IDisposable。
  • @Remus:不幸的是,我们目前还不知道 Database 类。 @Matt:我们需要更多信息。
  • 是 EnterpriseLibrary,马特? msdn.microsoft.com/en-us/library/dd203144.aspx
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多