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: 即使将其包装在使用中,我仍然可以看到使用的连接(我关闭了连接池):

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

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

4

5 回答 5

20

C# 的垃圾收集是非确定性的,但该语言确实为资源处理提供了确定性结构,如下所示:

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

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

于 2009-10-12T03:36:56.600 回答
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()方法是一种奇怪的野兽:

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

所以这意味着数据适配器应该为你处理它,如果你从一个封闭的连接开始。我更担心您将 sql 命令作为纯字符串传递。您的查询中必须不时包含用户参数,这意味着您将这些数据直接连接到命令字符串中。 不要那样做!! 改为使用 SqlCommand 的参数集合。

于 2009-10-12T03:39:46.377 回答
2

我同意这里的所有答案,加上连接可能不仅仅是一种方法,范围更广。当您需要在不同的地方使用现有连接时,场景会发生一些变化。始终确保在使用完它们后调用Dispose所有实现的对象。IDisposable这是一个很好的做法,因此您不会得到垃圾收集器无法决定如何处理它们的未使用对象。

于 2009-10-12T04:01:44.443 回答
1

它将是,在垃圾收集完成它的工作之后。打开文件流进行写入而不关闭它也是如此。即使代码超出范围,它也可能被“锁定”。

于 2009-10-12T03:37:04.230 回答
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;
    }
}

于 2009-10-12T03:39:58.497 回答