.net SqlConnection即使在使用{}内也没有关闭

时间:2008-11-06 14:57:33

标签: c# .net linq-to-sql sqlconnection

请帮忙!

背景信息

我有一个访问SQL Server 2005数据库的WPF应用程序。数据库在运行应用程序的计算机上本地运行。

我到处使用Linq DataContext我使用了一个using {}语句,并传入一个函数的结果,该函数返回一个已打开的SqlConnection对象,并在返回DataContext构造函数之前使用它执行了一个SqlCommand ..即

// In the application code
using (DataContext db = new DataContext(GetConnection()))
{
    ... Code 
}

其中getConnection看起来像这样(我从函数中删除了'fluff'以使其更具可读性,但没有其他功能缺失)。

// Function which gets an opened connection which is given back to the DataContext constructor
public static System.Data.SqlClient.SqlConnection GetConnection()
{
   System.Data.SqlClient.SqlConnection Conn = new System.Data.SqlClient.SqlConnection(/* The connection string */);

    if ( Conn != null )
    {
        try
        {
            Conn.Open();
        }
        catch (System.Data.SqlClient.SqlException SDSCSEx)
        {
             /* Error Handling */
        }

        using (System.Data.SqlClient.SqlCommand SetCmd = new System.Data.SqlClient.SqlCommand())
        {
            SetCmd.Connection = Conn;
            SetCmd.CommandType = System.Data.CommandType.Text;

            string CurrentUserID = System.String.Empty;
            SetCmd.CommandText = "DECLARE @B VARBINARY(36); SET @B = CAST('" + CurrentUserID + "' AS VARBINARY(36)); SET CONTEXT_INFO @B";

            try
            {
                SetCmd.ExecuteNonQuery();
            }
            catch (System.Exception)
            {
                /* Error Handling */
            }
        }

        return Conn;
    }

我不认为作为WPF的应用程序对我遇到的问题有任何影响。

我遇到的问题

尽管SqlConnection与Sql Server Management Studio中的DataContext一起处理,我仍然可以看到大量的打开连接:

status : 'Sleeping' 
command : 'AWAITING COMMAND' 
last SQL Transact Command Batch : DECLARE @B VARBINARY(36); SET @B = CAST('GUID' AS VARBINARY(36)); SET CONTEXT_INFO @B

最终连接池用完了,应用程序无法继续。

所以我只能得出结论,以某种方式运行SQLCommand来设置Context_Info意味着当DataContext被释放时连接不会被处理掉。

当有人使用它们的DataContext时,是否可以发现任何显而易见的阻止连接被关闭和处理的东西?

6 个答案:

答案 0 :(得分:19)

来自MSDNDataContext Constructor (IDbConnection)):

  

如果你提供一个开放的连接,那么   DataContext不会关闭它。   因此,不要实例化   具有开放连接的DataContext   除非你有充分的理由去做   此

所以基本上,看起来你的连接正在等待GC在它们被释放之前完成它们。如果你有很多代码可以做到这一点,一种方法可能是在数据上下文的部分类中覆盖Dispose(),并关闭连接 - 只需确保记录数据上下文假定连接的所有权!

    protected override void Dispose(bool disposing)
    {
        if(disposing && this.Connection != null && this.Connection.State == ConnectionState.Open)
        {
            this.Connection.Close();
            this.Connection.Dispose();
        }
        base.Dispose(disposing);
    }

就个人而言,只要我“使用”连接(允许我执行多项操作),我很乐意给它(常规数据上下文,没有上面的黑客)开放连接 - 即

using(var conn = GetConnection())
{
   // snip: some stuff involving conn

   using(var ctx = new FooContext(conn))
   {
       // snip: some stuff involving ctx
   }

   // snip: some more stuff involving conn
}

答案 1 :(得分:7)

LINQ SqlProvider使用的DataContext仅关闭SQL连接(通过SqlConnectionManager.DisposeConnection),如果它是打开它的那个。如果您向SqlConnection构造函数提供已打开的DataContext对象,则不会为您关闭它。因此,你应该写:

using (SqlConnection conn = GetConnection())
using (DataContext db = new DataContext(conn))
{
    ... Code 
}

答案 2 :(得分:3)

我使用实体框架遇到了同样的问题。我的ObjectContextusing块包裹着。

当我调用SaveChanges()时建立了连接,但在using语句超出范围后,我注意到SQL Management Studio仍然有一个"AWAITING COMMAND"用于.NET SQL客户端。 看起来这与ADO.NET提供程序的行为有关,该提供程序默认启用了连接池。

来自{<3}}上的“使用与SQL Server的连接池”(强调我的):

  

连接池减少了需要打开新连接的次数。 pooler保持物理连接的所有权。它通过为每个给定的连接配置保持一组活动连接来管理连接。每当用户在连接上调用Open时,pooler会查看池中是否存在可用连接。如果池连接可用,则将其返回给调用者,而不是打开新连接。 当应用程序在连接上调用Close时,pooler会将其返回到池化的活动连接集,而不是实际关闭它。将连接返回到池后,就可以在下一次Open呼叫时重复使用。

如果需要,MSDNClearAllPools对于显式关闭所有池化连接似乎也很有用。

答案 3 :(得分:1)

我认为连接虽然不再被引用,但正在等待GC完全处理它。

解决方案:

创建自己的DataContext类,该类派生自自动生成的类。 (重命名基础,这样您就不必更改任何其他代码)。

在派生的DataContext中 - 添加Dispose()函数。在那 - 配置内部连接。

答案 4 :(得分:1)

非常感谢帮助人员,现在已经解决了..

基本上我采用了上面大部分答案的元素并实现了上面的DataContext构造函数(我已经重载了构造函数,因此它不是一个很大的改变)。

// Variable for storing the connection passed to the constructor
private System.Data.SqlClient.SqlConnection _Connection;

public DataContext(System.Data.SqlClient.SqlConnection Connection) : base(Connection)
{
    // Only set the reference if the connection is Valid and Open during construction
    if (Connection != null)
    {
        if (Connection.State == System.Data.ConnectionState.Open)
        {
            _Connection = Connection;                    
        }
    }           
}

protected override void Dispose(bool disposing)
{        
    // Only try closing the connection if it was opened during construction    
    if (_Connection!= null)
    {
        _Connection.Close();
        _Connection.Dispose();
    }

    base.Dispose(disposing);
}

执行此操作而不是上述某些建议的原因是,在dispose方法中访问this.Connection会抛出 ObjectDisposedException

以上的作品和我希望的一样!

答案 5 :(得分:0)

Dispose应关闭连接,MSDN指出:

  

如果SqlConnection没用了   范围,它不会被关闭。因此,   你必须明确关闭   通过调用Close或者连接   处置。关闭和处理是   在功能上等同。如果   连接池值池化   设置为true或是,底层   连接返回到   连接池。另一方面,如果   Pooling设置为false或no ,.   与服务器的底层连接是   闭合。

我的猜测是你的问题与GetContext()有关。