SQL Server 连接超时以及将语句与命令和连接对象一起使用

本文关键字:连接 命令 对象 一起 语句 超时 Server SQL | 更新日期: 2023-09-27 18:36:43

请在回答之前阅读整个问题。 我很抱歉,我似乎从不写简短的问题......

我支持一个C#内部Web应用程序,它命中在Windows Small Business Server 2011 SP1盒子上运行的SQL Server 2008 R2。

我们最近遇到很多SQL超时,这里有一个示例异常:

System.Web.HttpUnhandledException:抛出类型为"System.Web.HttpUnhandledException"的异常。 ---> System.InvalidOperationException: 超时已过期。从池获取连接之前经过的超时期限。这可能是因为所有池连接都在使用中,并且已达到最大池大小。 at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) at System.Data.SqlClient.SqlConnection.Open()

我已经检查了几件事,其中之一是代码如何处理连接和关闭连接。 我在其他线程中读到,在您的连接中使用 Using 语句就足够了,因为它"......包装连接创建在尝试..最后,并将连接处置调用放在最后"。 即使在发生异常时,连接也会关闭。

所以,我同意并多年来一直使用这种方法。 其他人建议显式关闭连接,即使对连接使用 Using 语句也是如此。我认为这将是多余的...

但是,我的问题是关于命令对象的。 其他人为这个应用程序编写了一个大型数据库方法库,他们(在所有数据库方法中)在 SqlConnection 对象使用 语句之前声明了 SqlCommand 对象。 他们还在连接 using 语句之前将连接对象分配给命令对象。

在连接 using 语句中声明和使用命令对象是否更好,并且以另一种方式执行此操作是否会导致 sql 连接超时(除非 sql 连接超时的其他原因)? 以这段代码为例:

    public Musician GetMusician(int recordId)
    {
        Musician objMusician = null;
        SqlConnection con = new SqlConnection(_connectionString);
        SqlCommand cmd = new SqlCommand();
        cmd.Connection = con;
        cmd.CommandText = "selectMusician";
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@id", recordId);
        using (con)
        {
            con.Open();
            SqlDataReader reader = cmd.ExecuteReader();
            if (reader.HasRows)
            {
                reader.Read();
                objMusician = new Musician((int)reader["id"]);
                objMusician.Name = (string)reader["name"];
            }
        }
        if objMusician != null)
        {
            objMusician.Albums = Albums.GetAlbums((int)objMusician.ID);
            objMusician.Tours = Tours.GetTours((int)objMusician.ID);
            objMusician.Interviews = Interviews.GetInterviews((int)objMusician.ID);
        }
        return objMusician;
    }

还要知道调用页面中有 try 捕获,并且是将错误记录到我们的日志记录数据库的页面。 我们让异常冒泡到页面上的调用方法,然后在那里进行处理。

SQL Server 连接超时以及将语句与命令和连接对象一起使用

完成后,应显式关闭连接。您永远不会关闭任何连接,因此在达到连接池限制后,您将收到错误,直到您手动回收池或它自行循环。将属性分配块移动到使用块内并执行一个骗局。关闭();cmd.Dispose();在返回您的 obj音乐家之前:

using (con)
{
    con.Open();
    SqlDataReader reader = cmd.ExecuteReader();
    if (reader.HasRows)
    {
        reader.Read();
        objMusician = new Musician((int)reader["id"]);
        objMusician.Name = (string)reader["name"];
    }
    if objMusician != null)
    {
        objMusician.Albums = Albums.GetAlbums((int)objMusician.ID);
        objMusician.Tours = Tours.GetTours((int)objMusician.ID);
        objMusician.Interviews = Interviews.GetInterviews((int)objMusician.ID);
    }
    con.Close();
    cmd.Dispose();        
    return objMusician;
}

不知道它是否有助于您的超时问题,但我总是像下面这样构建我的代码并且没有这个问题:

using(var cmd = new SqlCommand())
{
    using(var con = new SqlConnection(ConnectionString))
    {
        con.Open();
        cmd.Connection = con;
        cmd.CommandText = "selectMusician";
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@id", recordId);
        ...
    }
}

刚刚在MSDN上阅读,它说"当您完成使用组件时调用Dispose。方法使组件处于不可用状态。调用 Dispose 后,必须释放对组件的所有引用,以便垃圾回收器可以回收组件占用的内存。这意味着为了使 GC 立即收集连接,您必须在释放命令之前释放连接,否则连接将挂起,直到 GC 开始调用 Finalize。

按如下方式重构方法。您可能会遇到这样一种情况:数据读取器具有对连接的引用,但尚未释放。

public Musician GetMusician(int recordId)
{
    Musician objMusician = null;
    using(SqlConnection con = new SqlConnection(_connectionString))
    {
        con.Open();
        using (SqlCommand cmd = new SqlCommand())
        {
            cmd.Connection = con;
            cmd.CommandText = "selectMusician";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue("@id", recordId);
            using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
            {
                if (reader.HasRows)
                {
                    reader.Read();
                    objMusician = new Musician((int) reader["id"]);
                    objMusician.Name = (string) reader["name"];
                }
                if objMusician != null)
                {
                    objMusician.Albums = Albums.GetAlbums((int)objMusician.ID);
                    objMusician.Tours = Tours.GetTours((int)objMusician.ID);
                    objMusician.Interviews = Interviews.GetInterviews((int)objMusician.ID);
                }
            }
        }
        return objMusician;
    }
}