简体   繁体   English

.Net数据库如何正确关闭我的数据库连接?

[英].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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM