[英].Net Database how do I properly close my database connections?
So I've read a lot about SqlDataReaders not being disposed of properly in .Net - and I've been fighting the "Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached" error for a couple days now. 所以我已经阅读了很多关于SqlDataReaders没有在.Net中正确处理的内容 - 而且我一直在与“Timeout expired”进行斗争。在从池中获取连接之前已经过了超时时间。这可能是因为所有池连接都发生了正在使用并且达到最大池大小“现在错误了几天。 Obviously I could jack the max pool size up to 30,000 - but that doesn't address the actual problem.
显然,我可以将最大池大小提升到30,000 - 但这并不能解决实际问题。
As I step through the code, I execute the following SQL Query: 当我单步执行代码时,我执行以下SQL查询:
select * from sys.dm_os_performance_counters
where counter_name ='User Connections'
After the 之后
cmd.Connection.Open();
line, the User Connections are incremented by 1. However, it NEVER goes back down unless I recycle the app pool on the web server (at which point all active database connections from the website are killed). 行,用户连接增加1.但是,除非我在Web服务器上回收应用程序池(此时,网站上的所有活动数据库连接都被终止),否则它永远不会回落。
Here is my code: 这是我的代码:
public static DataTable SPExecuteDataTable(string[] ConnectionData, params object[] args)
{
SqlConnection conn = null;
SqlCommand cmd = null;
SqlDataReader dr = null;
try
{
conn = new SqlConnection(ConnectionData[1]);
cmd = new SqlCommand(ConnectionData[0], new SqlConnection(ConnectionData[1]));
cmd.CommandType = CommandType.StoredProcedure;
for (int i = 0; i < args.Length; i++)
{
SqlParameter Param = new SqlParameter(ConnectionData[i + 2], DBNullIfNull(args[i]));
cmd.Parameters.Add(Param);
}
cmd.Connection.Open();
DataTable dt = new DataTable();
using (dr = cmd.ExecuteReader())
{
if (dr != null)
dt.Load(dr);
else
dt = null;
}
return dt;
}
catch (Exception e)
{
Exception x = new Exception(String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message));
throw x;
}
finally
{
conn.Close();
cmd.Connection.Close();
dr.Close();
conn.Dispose();
cmd.Dispose();
dr.Dispose();
}
So far, I've tried explicitly closing the connections (like in my finally block), but that doesn't work. 到目前为止,我已经尝试明确关闭连接(就像在我的finally块中),但这不起作用。 I've also tried using statements like so:
我也试过使用这样的语句:
using (SqlDataReader dr = blah blah blah)
{
//code here
}
But that also doesn't work. 但这也行不通。 What is wrong with my code, here?
我的代码出了什么问题,在这里?
The preferred practice is to wrap connections, commands, and readers in using
blocks: 首选实践是
using
块包装连接,命令和读取器:
using(SqlConnection conn = new SqlConnection(ConnectionData[1])
{
using(SqlCommand cmd = new SqlCommand(ConnectionData[0], conn)
{ // ^-- re-use connection - see comment below
cmd.CommandType = CommandType.StoredProcedure;
for (int i = 0; i < args.Length; i++)
{
SqlParameter Param = new SqlParameter(ConnectionData[i + 2], DBNullIfNull(args[i]));
cmd.Parameters.Add(Param);
}
cmd.Connection.Open();
DataTable dt = new DataTable();
using (dr = cmd.ExecuteReader())
{
if (dr != null)
dt.Load(dr);
else
dt = null;
}
return dt;
}
}
that way they all get closed and disposed of properly. 这样他们都会被关闭并妥善处理。
Although I think the heart of your issue is that you're creating two connections each time: 虽然我认为你问题的核心在于你每次创建两个连接:
conn = new SqlConnection(ConnectionData[1]);
cmd = new SqlCommand(ConnectionData[0], new SqlConnection(ConnectionData[1]));
^---- creating a second connection
Finally, you are losing a lot of potentially valuable information (stack trace, etc.) by creating a new exception and throwing it rather than re-throwing the original exception: 最后,您通过创建新异常并抛出它而不是重新抛出原始异常,丢失了许多可能有价值的信息(堆栈跟踪等):
catch (Exception e)
{
Exception x = new Exception(String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message));
throw x;
}
I would either let the original exception bubble up or include the original exception as the InnerException
: 我要么让原始异常冒泡,要么包含原始异常作为
InnerException
:
catch (Exception e)
{
string message = String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message);
Exception x = new Exception(message, e);
throw x;
}
Solution: 解:
Use DataTables! 使用DataTables! To prevent having non-data-access layers of your application from having to communicate with the database, just do this in your data-access layer:
要防止应用程序的非数据访问层与数据库通信,只需在数据访问层中执行此操作:
using (SqlDataReader dr = cmd.ExecuteReader())
{
if (dr != null)
dt.Load(dr);
else
dt = null;
}
return dt;
Then, you can manipulate the dt however you'd like in the rest of your solution, and the connections HAVE ALREADY BEEN properly disposed. 然后,您可以在解决方案的其余部分中操纵dt,并且已经正确处理了连接。 Works like a charm and, luckily, the code for a datatable and datareader are very similar, so modifying the application in this manner was relatively painless.
像魅力一样工作,幸运的是,数据表和datareader的代码非常相似,因此以这种方式修改应用程序是相对轻松的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.