首页 > 解决方案 > 让 .net SqlConnection 无限期打开与 using 块

问题描述

我有一个深入的问题。我想了解 aSqlConnection关闭的原因以及如何检测连接池是否实际发生。

我有两个不同版本的类,它们抽象与数据库对话并返回DataTable. 我提供的是问题/问题的一个例子。

版本 1SqlConnection在实例化时打开 a 并使其保持打开状态。这是一个单线程进程,它处理传入的消息并将结果尽快写入数据库。SqlConnection这就是永久开放背后的想法:

public class SpecialDbClientVersion1 : IDisposable
{
    string connStr;
    SqlConnection dbConn;

    public SpecialDbClientVersion1(string connStr)
    {
        this.connStr = connStr;
        this.dbConn = new SqlConnection(connStr);

        try
        {
            dbConn.Open();
        }
        catch (SqlException ex)
        {
            throw new Exception($"An exception occurred while trying to connect to {dbConn.Database}", ex);
        }
    }

    public DataTable QuerySqlReturnResultAsDataTable(string sql, int commandTimeout = 30)
    {
        try
        {
            var cmd = new SqlCommand(sql, dbConn);
            cmd.CommandType = CommandType.Text;
            cmd.CommandTimeout = commandTimeout;
            var dt = new DataTable();
            var adapter = new SqlDataAdapter(cmd);
            adapter.Fill(dt);
            return dt;
        }
        catch (Exception e)
        {
            throw new InvalidOperationException(e.Message);
        }
    }

    public void Dispose()
    {
        dbConn.Dispose();
    }
}

当我对此进行测试时,一切都很好,并且吞吐量非常高。最终虽然由于SqlConnection某种原因关闭了。抛出错误,聚会结束。

问题 1:有人可以解释什么会导致此连接进入Closed状态而无需我调用该Close()方法?

现在版本 2 在一个using块中执行所有工作。根据互联网,运行时应该在后台神奇地使用连接池。SqlConnection这是按照 Microsoft处理对象的最佳实践。使用连接池应该不会对性能造成太大影响。

public class SpecialDbClientVersion2
{
    string connStr;
    SqlConnection dbConn;

    public SpecialDbClientVersion2(string connStr)
    {
        this.connStr = connStr;
    }

    public DataTable QuerySqlReturnResultAsDataTable(string sql, int commandTimeout = 30)
    {
        using(var dbConn = new SqlConnection(connStr))
        {
            try
            {
                var cmd = new SqlCommand(sql, dbConn);
                cmd.CommandType = CommandType.Text;
                cmd.CommandTimeout = commandTimeout;
                var dt = new DataTable();
                var adapter = new SqlDataAdapter(cmd);
                adapter.Fill(dt);
                return dt;
            }
            catch (Exception e)
            {
                throw new InvalidOperationException(e.Message);
            }
        }
    }
}

问题 2:如何判断连接池是否实际被使用?写入数据库需要尽可能快,我担心版本 2 每次都必须重新建立与数据库的连接。这会破坏性能。如何衡量连接池是否正在发生以及它是否影响服务的吞吐量?

我一直在寻找有关此主题的 Deep Dive 资源,但我发现的大部分内容只是“在一个using块中使用 SqlConnection,一切都会好起来的”。我正在寻找比这更深层次的解释。我真的很想了解这里发生的事情的本质,以便我寻找正确的性能问题。感谢您提供的任何见解。

标签: c#.netsql-serversqlclient

解决方案


问题 1 可以通过与池相关的空闲超时来回答。

问题2:连接池默认设置为true,所以如果你没有在连接字符串中明确关闭它,你可以假设你正在使用它。我认为没有办法确定池中还剩下多少连接。如果您每次都使用完全相同的连接字符串,那么您可以放心地假设它们在同一个池中。

阅读此文档以更深入地了解连接池。

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling

您必须在 SQL Server 级别监视连接。

SELECT 
DB_NAME(dbid) as DBName, 
COUNT(dbid) as NumberOfConnections,
loginame as LoginName
FROM sys.sysprocesses
WHERE bid > 0
GROUP BY dbid, loginame;

推荐阅读