【问题标题】:Why is my code leaking connections?为什么我的代码会泄漏连接?
【发布时间】:2012-08-08 09:00:05
【问题描述】:

问题:

为什么下面的代码会泄露连接?

    public System.Data.DataTable GetDataTable()
    {
        System.Data.DataTable dt = new System.Data.DataTable();
        string strConnectionString = "Data Source=localhost;Initial Catalog=MyDb;User Id=SomeOne;Password=TopSecret;Persist Security Info=False;MultipleActiveResultSets=False;Packet Size=4096;";
        System.Data.SqlClient.SqlConnectionStringBuilder csb = new System.Data.SqlClient.SqlConnectionStringBuilder(strConnectionString);
        csb.IntegratedSecurity = true;


        string strSQL = "SELECT * FROM T_Benutzergruppen";

        using (System.Data.SqlClient.SqlConnection sqlcon = new System.Data.SqlClient.SqlConnection(csb.ConnectionString))
        {
            using (System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand(strSQL, sqlcon))
            {
                if (sqlcon.State != System.Data.ConnectionState.Open)
                {
                    sqlcon.Open();
                }

                // First attempt
                //System.Data.SqlClient.SqlDataAdapter sqlda = new System.Data.SqlClient.SqlDataAdapter("SELECT * FROM T_Benutzer", sqlcon);
                //sqlda.Fill(dt);

                cmd.ExecuteNonQuery();
            }

            if(sqlcon.State != System.Data.ConnectionState.Closed)
                sqlcon.Close();
        }
        //sqlcon.ConnectionString = csb.ConnectionString;

        // Second attempt
        //System.Data.SqlClient.SqlDataAdapter sqlda = new System.Data.SqlClient.SqlDataAdapter("SELECT * FROM T_Benutzer", csb.ConnectionString);
        //sqlda.Fill(dt);


        return dt;
    }

如果我进入 SQL-Server 活动监视器,我会看到 Session 68

SELECT * FROM T_Benutzergruppen

补充问题:

如果有问题:

如果我注释掉除了 ConnectionStringBuilder 之外的所有内容,并且只在这个函数中执行下面的代码,为什么它也会泄漏连接?

// Second attempt
System.Data.SqlClient.SqlDataAdapter sqlda = new System.Data.SqlClient.SqlDataAdapter("SELECT * FROM T_Benutzer", csb.ConnectionString);
sqlda.Fill(dt);

注意:
executenonquery 没有任何意义,它只是用于测试目的。

如果我让它在调试器中运行,我会看到 sqlcon.Close();

get 被执行了,所以问题不在于

if(sqlcon.State != System.Data.ConnectionState.Closed)

【问题讨论】:

  • 您不需要在您的SqlConnection 对象上显式调用.Close()using 块会在超出范围时为您执行此操作。您的代码很好,正如其他人已经指出的那样,这只是工作中的连接池。
  • @James:是的。现在我知道默认情况下池是打开的。

标签: c# .net sql ado.net database-connection


【解决方案1】:

连接池。不用担心。

这是正常行为。

http://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.100).aspx

【讨论】:

  • 好的,谢谢。设置 csb.Pooling = false;解决了这两个问题。现在我终于可以在其余代码中搜索连接泄漏了。
  • @Quandary:确保您没有在生产设置中关闭连接池。那会杀死性能。
  • @Eric J.:别担心,我知道。只是调试池以外的泄漏,池连接妨碍了我在 ActivityMonitor 中搜索泄漏位置。
【解决方案2】:

ADO.Net 将连接池化,以便它们可以重复使用,因为它们的创建成本相对较高。

连接到数据库服务器通常包括几个耗时的步骤。必须建立诸如套接字或命名管道之类的物理通道,必须发生与服务器的初始握手,必须解析连接字符串信息,必须由服务器验证连接,必须运行检查以登记当前交易等等。

实际上,大多数应用程序只使用一种或几种不同的连接配置。这意味着在应用程序执行期间,许多相同的连接将被重复打开和关闭。为了最大限度地降低打开连接的成本,ADO.NET 使用了一种称为连接池的优化技术。

http://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.100).aspx

此外,无需显式调用 .Close()。您的 using 块将调用 IDisposable.Dispose(),这将正确关闭连接。

【讨论】:

  • 我只是不知道默认情况下池是打开的。以为是我的错。
  • 是的,这是明智的默认设置。不要在生产代码中关闭它:-)
  • 哦,谁在乎,生产服务器不是我的部门;)