【问题标题】:.Net Database how do I properly close my database connections?.Net 数据库如何正确关闭我的数据库连接?
【发布时间】:2014-11-04 14:28:36
【问题描述】:

所以我已经阅读了很多关于 SqlDataReaders 没有在 .Net 中正确处理的信息 - 我一直在与“超时已过期。在从池中获取连接之前已经过的超时时间。这可能是因为所有池连接都在使用中,并且达到了最大池大小”错误几天了。显然,我可以将最大池大小提升到 30,000 - 但这并不能解决实际问题。

在单步执行代码时,我执行以下 SQL 查询:

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

之后

cmd.Connection.Open(); 

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

这是我的代码:

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
}

但这也行不通。我的代码有什么问题,在这里?

【问题讨论】:

    标签: c# sql asp.net database-connection


    【解决方案1】:

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

    【讨论】:

    • 谢谢,试试看。为了澄清,冗余连接(你的帖子结束)不是原始设置。在对问题进行故障排除时,我将其更改为。我只是更改了 cmd = 代码行并将“new sqlcon ...”替换为“conn” - 问题仍然存在。
    • 非常感谢 - 仍在测试这个...另外,您能解释一下在使用嵌套 using 语句时如何从函数返回 SqlDataReader 吗?他们还会自行处置吗?
    • 如果您要退货,您不会将其放入 using 中。在该模式中,调用者有责任处置读者,这就是为什么返回读者不是最佳实践的原因——它将创建对象的责任与处置它分开。
    • 虽然这并没有完全解决问题,但它已经大大减少了它。连接仍在堆积,尽管比以前少得多。鉴于您发布的代码,您知道为什么某些连接仍会持续存在吗?
    • @Stan 不知道ConnectionData[1] 中有什么。连接是池化的,所以只要你的连接字符串没有改变,你就不应该泄露连接。
    【解决方案2】:

    解决方案:

    使用数据表!要防止应用程序的非数据访问层与数据库通信,只需在数据访问层中执行此操作:

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

    然后,您可以在解决方案的其余部分中随意操作 dt,并且已经正确处理了连接。像魅力一样工作,幸运的是,数据表和数据读取器的代码非常相似,因此以这种方式修改应用程序相对容易。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-30
      • 1970-01-01
      • 2021-04-27
      • 1970-01-01
      相关资源
      最近更新 更多