代码之家  ›  专栏  ›  技术社区  ›  choudeshell

为什么不释放/关闭sqlconnection?

  •  5
  • choudeshell  · 技术社区  · 17 年前

    给定方法:

    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: 在我从这里得到的帮助下进行更多的调试——答案是有人用池硬编码了一个连接字符串。谢谢你的帮助-如果可以的话,我会把所有的回答标记为答案。

    5 回复  |  直到 17 年前
        1
  •  19
  •   Andrew Hare    17 年前

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

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

    这将创建一个 try/finally 块,它将确保不管方法中发生了什么,都会释放连接对象。您真的应该包装实现 IDisposable 在这样的一个使用块中,它将确保负责任的资源管理(对非托管资源,如数据库连接),并为您提供所需的确定性控制。

        2
  •  2
  •   Joel Coehoorn    17 年前

    因为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() method is a strange beast:

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

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

        3
  •  1
  •   o.k.w    17 年前

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

        4
  •  1
  •   Anthony Shaw    17 年前

    我认为这与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;
        }
    }
    
    
        5
  •  1
  •   red.clover    17 年前

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

    推荐文章