2008-11-06 67 views
16

请帮忙!.net SqlConnection即使在使用时也不会被关闭{}

背景信息

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

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

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

其中的getConnection看起来像这样(我已经去掉了从功能的“绒毛”,以使其更具可读性,但就是缺少无其他功能)。

// 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一个对我有问题的任何影响。

随着DataContext的在SQL Server Management Studio中我仍然可以看到打开的连接负载与布置我有

尽管SqlConnection的问题:

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被丢弃时,会阻止连接被关闭和处置吗?

回答

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); 
    } 

个人而言,我会愉快地给它(常规数据上下文,W/O上面的黑客),只要打开的连接,因为我是“使用”连接(允许我执行多个操作) - 即

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 
} 
0

Dispose应关闭的连接,为MSDN指出:如果关闭SqlConnection超出 范围

,它不会被关闭。因此, 您必须通过调用Close或 Dispose来明确关闭 连接。 Close and Dispose在功能上等同于 。如果 连接池值设置为true或是,则将底层 连接返回到连接池 连接池 。另一方面,如果 池设置为false或否, 底层连接到服务器是关闭 。

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

7

由LINQ DataContext使用的SqlProvider只关闭SQL连接(通过SqlConnectionManager.DisposeConnection),如果它是打开它的人。如果您将已经打开的SqlConnection对象给予DataContext构造函数,它将不会为您关闭它。因此,你应该写:

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

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

解决方案:

创建自己的DataContext类从自动生成一个派生的。 (重新命名基本版本,以便不必更改任何其他代码)。

在你的派生的DataContext中 - 添加一个Dispose()函数。在那 - 处理内部连接。

+0

您可以添加一个部分类来扩展自动生成的数据上下文;无需子类。 – 2008-11-06 15:10:22

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

而上述作品以及我希望!

+0

嘿,这就是我所说的。我一定很聪明。 – GeekyMonkey 2008-11-06 21:57:54

3

我遇到了使用实体框架的相同问题。我的ObjectContext缠绕在using区块。

当我调用SaveChanges()时建立了一个连接,但在using声明超出范围之后,我注意到SQL Management Studio对于.NET SQL Client仍然有"AWAITING COMMAND"。 看起来这与ADO.NET提供程序的行为有关,默认情况下会打开连接池。

从“使用连接与SQL Server池”上MSDN(重点煤矿):

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

而且ClearAllPoolsClearPool似乎有用的,如果需要明确地关闭所有连接池。

相关问题