为什么 SqlConnection 在强制执行 .Close() 和 using(...) 后仍然打开?

Why is the SqlConnection still open after forcing .Close() and with using(...)?

我正在对 ADO.Net 和 EF 进行一些试验,以便更好地了解它如何处理 SQL 服务器连接。

我在 ADO.Net 中发现了一些非常有趣的东西。我正在创建多个调用简单插入 SQL 脚本的任务,并适当等待以处理 SqlConnectionSqlCommand。这里没什么特别的,但是当 10k 任务完成处理时,所有 SQL 连接仍然挂起(我通过 运行 sp_who 确认)。清除这些连接的唯一方法是关闭应用程序实例。

这怎么可能?我尝试了很多方法来强制它关闭,= null 数据访问实例 + 强制 GC 但没有...

我试图从这种行为中理解,但我失败了。有什么线索吗?

static void Main(string[] args)
{
    Console.WriteLine(DateTime.Now.ToString("HH:mm:ss"));

    for (int i = 0; i < 10000; i++)
    {
        Task.Run(() =>
            {
                var dbLegacy = new DataAccessLegacy();
                dbLegacy.TableBInsert();
                dbLegacy = null;
            });
    }

    Console.ReadKey();
}

public void TableBInsert()
{
    using (SqlConnection connection = new SqlConnection(@"Password=qpqp;Persist Security Info=True;User ID=sqlUser2;Initial Catalog=DatabaseA;Data Source=VM2HOSTNAME\VM2INSTANCEA"))
    {
        using (SqlCommand command = new SqlCommand("DatabaseBInsert", connection))
        {
            command.CommandType = CommandType.StoredProcedure;

            command.Parameters.Add("ColAInt", SqlDbType.Int);
            command.Parameters[0].Value = (new Random()).Next(0, 5000);

            command.Parameters.Add("ColBTinyInt", SqlDbType.TinyInt);
            command.Parameters[1].Value = (new Random()).Next(1, 255);

            command.Parameters.Add("ColCVarchar", SqlDbType.VarChar);
            command.Parameters[2].Value = Convert.ToChar((new Random()).Next(1, 255)).ToString();

            command.Parameters.Add("ColDVarcharMax", SqlDbType.VarChar);
            command.Parameters[3].Value = Convert.ToChar((new Random()).Next(1, 255)).ToString();

            command.Parameters.Add("ColEDecimal", SqlDbType.Decimal);
            command.Parameters[4].Value = (new Random()).Next(0, 5000) + 0.5;

            command.Parameters.Add("ColFSmallInt", SqlDbType.SmallInt);
            command.Parameters[5].Value = (new Random()).Next(0, 5000);

            command.Parameters.Add("ColGDateTime", SqlDbType.DateTime);
            command.Parameters[6].Value = DateTime.Now;

            command.Parameters.Add("ColHChar", SqlDbType.Char);
            command.Parameters[7].Value = Convert.ToChar((new Random()).Next(1, 255)).ToString();

            command.Parameters.Add("ColINVarchar", SqlDbType.NVarChar);
            command.Parameters[8].Value = Convert.ToChar((new Random()).Next(1, 255)).ToString();

            command.Parameters.Add("ColJNChar", SqlDbType.NChar);
            command.Parameters[9].Value = Convert.ToChar((new Random()).Next(1, 255)).ToString();

            connection.Open();
            command.ExecuteScalar();
            connection.Close();

            command.Dispose();
        }

        connection.Dispose();
    }
}

默认情况下,ADO.Net 使用 connection pooling。来自文档(强调我的):

Connection pooling reduces the number of times that new connections must be opened. The pooler maintains ownership of the physical connection. It manages connections by keeping alive a set of active connections for each given connection configuration. Whenever a user calls Open on a connection, the pooler looks for an available connection in the pool. If a pooled connection is available, it returns it to the caller instead of opening a new connection. When the application calls Close on the connection, the pooler returns it to the pooled set of active connections instead of closing it. Once the connection is returned to the pool, it is ready to be reused on the next Open call.