为什么SqlConnection没有被释放/关闭?

6

给定以下方法:

internal static DataSet SelectDataSet(String commandText, DataBaseEnum dataBase)
{
    var dataset = new DataSet();

    SqlConnection sqlc = dataBase == DataBaseEnum.ZipCodeDb
                             ? new SqlConnection(ConfigurationManager.AppSettings["ZipcodeDB"])
                             : new SqlConnection(ConfigurationManager.AppSettings["WeatherDB"]);
    SqlCommand sqlcmd = sqlc.CreateCommand();
    sqlcmd.CommandText = commandText;
    var adapter = new SqlDataAdapter(sqlcmd.CommandText, sqlc);
    adapter.Fill(dataset);


    return dataset;
}

为什么在调用方法超出范围或sqlc没有更多引用后,sqlc(SqlConnection)没有被释放/关闭?

编辑1: 即使将其包装在using中,我仍然可以看到连接(我已将连接池关闭):

SELECT DB_NAME(dbid) as 'Database Name',
COUNT(dbid) as 'Total Connections'
FROM sys.sysprocesses WITH (nolock)
WHERE dbid > 0
GROUP BY dbid

编辑2: 在这里得到的帮助下进行了更多的调试 - 答案是有人硬编码了一个使用池化的连接字符串。感谢所有的帮助 - 如果可以的话,我会将所有的回答都标记为答案。

5个回答

20

C# 的垃圾回收是非确定性的,但该语言提供了一种确定性结构来处理资源释放,如下所示:

using (SqlConnection connection = new SqlConnection(...))
{
    // ...  
}

这将创建一个try/finally块,确保无论方法中发生什么情况,连接对象都将被处理。您应该像这样在using块中包装实现IDisposable的类型的任何实例,因为它将确保负责任的资源管理(例如数据库连接等未受管控资源),并且还为您提供了确定性控制。


3
因为C#是一种垃圾回收语言,且垃圾回收是不确定性的。事实上,你的Sqlconnection已经被处理了。只是你不能选择何时处理。
Sql连接是一种有限资源,很容易就会创建足够多的连接而用尽它们。应该像这样编写代码:
internal static DataSet SelectDataSet(String commandText, DataBaseEnum dataBase)
{
    var dataset = new DataSet();

    using (SqlConnection sqlc = dataBase == DataBaseEnum.ZipCodeDb
                             ? new SqlConnection(ConfigurationManager.AppSettings["ZipcodeDB"])
                             : new SqlConnection(ConfigurationManager.AppSettings["WeatherDB"]))
    using (SqlCommand sqlcmd = sqlc.CreateCommand())
    {
        sqlcmd.CommandText = commandText;
        var adapter = new SqlDataAdapter(sqlcmd.CommandText, sqlc);
        adapter.Fill(dataset);

    }
    return dataset;
}

虽然在这种情况下你可能可以得到宽恕,因为.Fill()方法是一个奇怪的野兽:

如果IDbConnection在调用Fill之前关闭,它会被打开以检索数据,然后关闭。

因此,如果您从一个关闭的连接开始,数据适配器应该为您处理它。我更担心的是您将SQL命令作为普通字符串传递。有时查询中需要用户参数,这意味着您直接将数据串联到命令字符串中。不要那样做!改用SqlCommand的Paramters集合。


2

我同意这里所有的答案,还有一个连接可能比仅仅是一个方法具有更广泛的范围。当您需要在不同地方使用现有连接时,情况会有所改变。始终确保在使用完对象后调用实现IDisposable的所有对象的Dispose方法。这是一种良好的做法,可以避免出现未使用的对象,垃圾收集器无法决定如何处理它们。


1

我相信这与SqlConnection池有关。在工作中,我们经常使用using语句将整个调用包装起来,这会导致它调用dispose()方法,从而关闭连接并处理对象。

然后,您可以像这样进行操作:


internal static DataSet SelectDataSet(String commandText, DataBaseEnum dataBase)
{
    var dataset = new DataSet();

    using(SqlConnection sqlc = dataBase == DataBaseEnum.ZipCodeDb
                             ? new SqlConnection(ConfigurationManager.AppSettings["ZipcodeDB"])
                             : new SqlConnection(ConfigurationManager.AppSettings["WeatherDB"])) {
        SqlCommand sqlcmd = sqlc.CreateCommand();
        sqlcmd.CommandText = commandText;
        var adapter = new SqlDataAdapter(sqlcmd.CommandText, sqlc);
        adapter.Fill(dataset);


        return dataset;
    }
}


1

垃圾回收完成后就会这样。打开文件流进行写入但不关闭也是一样的。即使代码超出作用域,它也可能被“锁定”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接