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

本文关键字:数据库连接 何正确 数据库 Net | 更新日期: 2023-09-27 18:28:58

所以我读了很多关于SqlDataReaders在.Net中没有得到正确处理的文章,我一直在与"超时已过期。从池中获取连接之前已过超时期"错误作斗争。这可能是因为所有池连接都在使用中,并且已达到最大池大小"错误已经持续了几天。显然,我可以将最大池大小提高到30000,但这并不能解决实际问题。

当我逐步完成代码时,我执行以下SQL查询:

select * from sys.dm_os_performance_counters
where counter_name ='User Connections'

之后

cmd.Connection.Open(); 

行中,用户连接将增加1。然而,除非我回收网络服务器上的应用程序池,否则它永远不会恢复(此时,来自网站的所有活动数据库连接都会被终止)。

这是我的代码:

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

到目前为止,我已经尝试过显式关闭连接(就像在finally块中一样),但这不起作用。我也尝试过使用这样的语句:

using (SqlDataReader dr = blah blah blah)
{
    //code here
}

但这也不起作用。我的代码出了什么问题,在这里?

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

首选做法是将连接、命令和读取器包装在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;
    }    
}

这样它们都会被关闭并得到妥善处理。

尽管我认为问题的核心是您每次创建两个连接:

conn = new SqlConnection(ConnectionData[1]);
cmd = new SqlCommand(ConnectionData[0], new SqlConnection(ConnectionData[1]));
                                        ^----  creating a second connection

最后,通过创建一个新异常并抛出它,而不是重新抛出原始异常,您正在丢失许多潜在的有价值的信息(堆栈跟踪等):

catch (Exception e)
{
    Exception x = new Exception(String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message));
    throw x;
}

我要么让原始异常冒泡,要么将原始异常包含为InnerException:

catch (Exception e)
{
    string message = String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message);
    Exception x = new Exception(message, e);
    throw x;
}

解决方案:

使用数据表!为了防止应用程序的非数据访问层不得不与数据库通信,只需在数据访问层中执行以下操作:

using (SqlDataReader dr = cmd.ExecuteReader())
                        {
                            if (dr != null)
                                dt.Load(dr);
                            else
                                dt = null;
                        }
                        return dt;

然后,您可以在解决方案的其余部分中按照自己的意愿操作dt,并且连接已经得到了适当的处理。工作起来很有魅力,幸运的是,数据表和数据读取器的代码非常相似,因此以这种方式修改应用程序相对来说是无痛的。