我有多个线程访问同一个数据库(具有相同的连接字符串)。每个帖子:
使用下面的代码在需要一个
时打开它自己的连接实例 try
{
wasOpened = connection.State == ConnectionState.Open;
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
}
catch (Exception ex)
{
throw new Exception(string.Format("Connection to data source {0} can not be established! Reason: {1} - complete stack {2}",
connection.Database, ex.Message, ex.StackTrace == null ? "NULL" : ex.StackTrace.ToString()));
}
到目前为止,我们已在2台服务器上测试了此代码,并且有一台服务器有时会在SqlConnection.Open方法中抛出异常。这是我们从catch块获得的异常消息:
无法建立与数据源xyz的连接!原因:操作无效。连接已关闭。 - 完整堆栈
在System.Data.SqlClient.SqlConnection.GetOpenConnection()
在System.Data.SqlClient.SqlConnection.get_Parser()
在System.Data.SqlClient.SqlConnection.Open()
检查SqlConnection.GetOpenConnection方法显示innerConnection
为空:
internal SqlInternalConnection GetOpenConnection()
{
SqlInternalConnection innerConnection = this.InnerConnection as SqlInternalConnection;
if (innerConnection == null)
{
throw ADP.ClosedConnectionError();
}
return innerConnection;
}
我不清楚:为什么连接池有时会给我切断的连接(innerConnection == null)?
编辑#1 :代码中没有静态属性 - 我们总是在适当的时候关闭连接,在我们的Close方法中使用wasOpened并且意味着:如果在调用Open时已经打开了连接,只需将其打开关闭,否则关闭它。但是,这与此问题中描述的问题无关(innerConnection == null)。
编辑#2 :服务器:SQL Server 2008 R2,Windows Server 2003.客户端:Windows Server 2003(代码在SSIS包自定义组件中运行)。连接字符串:Data Source=server_name;Initial Catalog=db_name;Integrated Security=SSPI;Application Name=app_name
答案 0 :(得分:5)
首先,请仔细阅读:SQL Server Connection Pooling (ADO.NET)
我会为你引用最重要的部分(我认为):
为每个唯一连接字符串创建连接池。当一个 创建池,创建并添加多个连接对象 池,以满足最小池大小要求。 根据需要将连接添加到池中,直到最大池 指定的大小(默认值为100)。连接被释放回来 当他们关闭或处置时进入游泳池。
当请求SqlConnection对象时,它是从池中获取的 如果有可用的连接。要使用,必须连接 不使用,具有匹配的事务上下文或与之无关 任何事务上下文,并具有到服务器的有效链接。
连接池通过以下方式满足连接请求 重新分配连接,因为它们被释放回池中。 如果 已达到最大池大小且没有可用的连接 可用,请求排队。然后,捣蛋者试图收回任何 连接,直到达到超时(默认为15秒)。 如果pooler在连接时间之前无法满足请求 out,抛出异常。
我们强烈建议您始终关闭连接 完成使用它,以便连接将返回到 池。您可以使用的Close或Dispose方法执行此操作 连接对象,或打开使用中的所有连接 C#中的语句,或Visual Basic中的Using语句。连接 未明确关闭的可能不会被添加或返回给 池。有关更多信息,请参阅using Statement(C#Reference)
简而言之:不要在连接池的范围内挖走,并在完成后立即关闭连接(fe via using-statement
)。
由于您不想throw your DB-Class into the garbage can,我建议您增加最大池大小和/或超时或禁用池,看看会发生什么。
<add name="theConnectionString" connectionString="Data Source=(local);
Database=AdventureWorks; Integrated Security=SSPI;
Max Pool Size=200; Pooling=True; Timout=60" />
您还应该尝试捕捉此特定错误并clear all connection pools:
System.Data.SqlClient.SqlConnection.ClearAllPools();
或者看看这些看起来很有希望的问题:
答案 1 :(得分:2)
我有多个线程访问同一个数据库(具有相同的连接字符串)。每个帖子:
- 使用相同的连接字符串
创建自己的SqlConnection实例- 使用下面的代码在需要一个
时打开它自己的连接实例 醇>
如果您遇到随机出现的问题,在您的情况下,根据您显示的代码,您可能会:
所有这一切......
您应该将SqlConnection
包装在using
语句中。这样,当您的代码或线程完成时,连接将被关闭。
using (SqlConnection connection = new SqlConnection(connectionString))
{
//... stuff
}
这样一来,连接就可以保证调用Dispose()
方法(无论如何内部调用Close()
)。这样,如果连接正在使用(可能是它),则可以将连接返回到池中。
答案 2 :(得分:2)
Tim Schmelter和Bryan Crosby提供的答案在指南,参考资料,最佳做法等方面非常有价值。不幸的是,这对我来说还不够,因为我不能在遗留代码中做出重大改变。
解决此特定问题的方法是使用相同的锁封装SqlConnection的Open和Close方法。请注意,它符合我们的情况,可能不适合其他人。
我真的很抱歉我现在无法深入研究这个问题,并找出问题的根源是我们的代码还是连接池不是完全线程安全的。我知道很可能源代码在我们的代码中。考虑到这一点,这个答案比真正的解决方案更具解决方法。
在任何人应用此解决方法之前,请阅读其他答案。