SQL 具有准备好的语句和事务的服务器连接池

SQL Server Connection Pools with Prepared Statements and Transactions

我希望在我的 .Net 应用程序中利用连接池,但对准备好的语句和事务的支持有一些担忧。尽管 .Net 框架似乎提供了 EnlistTransaction 方法来为具有连接池的事务提供支持,但我正在尝试确定是否存在对准备好的语句的类似支持......或者是否甚至需要这种支持。有谁知道这个问题的答案吗?

TL;DR - .Net SQL 服务器连接池无缝处理准备好的语句。

我创建了一个主程序来测试这个...

private static void TestPreparedStatement()
{
    const string sql = "INSERT INTO myTable(id, numInt) " +
                       "VALUES((SELECT MAX(id) " +
                               "FROM myTable) + 1, @numInt);";
    using (SqlConnection dbConn = new SqlConnection(s_connStr))
    {
        dbConn.Open();
        Console.WriteLine("Getting ready to prepare statement on connection with ID " + dbConn.ClientConnectionId);
        s_cmd = new SqlCommand(sql, dbConn);
        s_cmd.Parameters.Add("@numInt", SqlDbType.Int);
        s_cmd.Prepare();
        Console.WriteLine("The command has been prepared.");
    }

    for (int threadNdx = 0; threadNdx < 10; ++threadNdx)
    {
        Thread prepStmntThread = new Thread(ExecPrepStmnt)
        {
            Name = threadNdx.ToString()
        };

        prepStmntThread.Start();
    }
}

private static void ExecPrepStmnt()
{
    using (SqlConnection dbConn = new SqlConnection(s_connStr))
    {
        dbConn.Open();
        lock (s_cmd)
        {
            Console.WriteLine("Thread #" + Thread.CurrentThread.Name + " has ownership of the command.");
            s_cmd.Connection = dbConn;
            s_cmd.Parameters["@numInt"].Value = int.Parse(Thread.CurrentThread.Name);
            Console.WriteLine("Thread #" + Thread.CurrentThread.Name + " is about to execute the command on the connection with ID " + s_cmd.Connection.ClientConnectionId + ".");
            try
            {
                s_cmd.ExecuteNonQuery();
                Console.WriteLine("Thread #" + Thread.CurrentThread.Name + " successfully executed the command.");
            }
            catch (Exception e)
            {
                Console.WriteLine("Thread #" + Thread.CurrentThread.Name + " failed to execute the command.");
                Console.WriteLine(e.Message);
            }

            Console.WriteLine("Thread #" + Thread.CurrentThread.Name + " is about to relinquish ownership of the command.");
        }

        Console.WriteLine("Thread #" + Thread.CurrentThread.Name + " is going to sleep with open pooled connection.");
        Thread.Sleep(5 * 1000);
    }

    Console.WriteLine("Thread #" + Thread.CurrentThread.Name + " has woken up and disposed of its pooled connection.");
}

输出是...

Getting ready to prepare statement on connection with ID 289ae798-7564-4b7c-b45f-60d09d54fa87
The command has been prepared.
Thread #0 has ownership of the command.
Thread #0 is about to execute the command on the connection with ID 289ae798-7564-4b7c-b45f-60d09d54fa87.
Thread #0 successfully executed the command.
Thread #0 is about to relinquish ownership of the command.
Thread #0 is going to sleep with open pooled connection.
Thread #2 has ownership of the command.
Thread #2 is about to execute the command on the connection with ID 2585e1fb-980a-45ee-9a59-17d94f44b2d5.
Thread #2 successfully executed the command.
Thread #2 is about to relinquish ownership of the command.
Thread #2 is going to sleep with open pooled connection.
Thread #1 has ownership of the command.
Thread #1 is about to execute the command on the connection with ID 2772d6a2-dc23-472d-bb4e-adfa99e97bdf.
Thread #1 successfully executed the command.
Thread #1 is about to relinquish ownership of the command.
Thread #1 is going to sleep with open pooled connection.
Thread #3 has ownership of the command.
Thread #3 is about to execute the command on the connection with ID 1ce7f447-f485-4b85-9ede-2854fc160a83.
Thread #3 successfully executed the command.
Thread #3 is about to relinquish ownership of the command.
Thread #3 is going to sleep with open pooled connection.
Thread #4 has ownership of the command.
Thread #4 is about to execute the command on the connection with ID 62d62a9c-b2e0-42e5-99da-3682cf7614ee.
Thread #4 successfully executed the command.
Thread #4 is about to relinquish ownership of the command.
Thread #4 is going to sleep with open pooled connection.
Thread #5 has ownership of the command.
Thread #5 is about to execute the command on the connection with ID cb62e434-534a-49a7-afbd-96e85f7e5888.
Thread #5 successfully executed the command.
Thread #5 is about to relinquish ownership of the command.
Thread #5 is going to sleep with open pooled connection.
Thread #6 has ownership of the command.
Thread #6 is about to execute the command on the connection with ID 946ccf21-a278-46ad-871f-5030ee00fcd0.
Thread #6 successfully executed the command.
Thread #6 is about to relinquish ownership of the command.
Thread #6 is going to sleep with open pooled connection.
Thread #7 has ownership of the command.
Thread #7 is about to execute the command on the connection with ID a4b36316-8f82-44ff-998b-f33b94b34320.
Thread #7 successfully executed the command.
Thread #7 is about to relinquish ownership of the command.
Thread #7 is going to sleep with open pooled connection.
Thread #8 has ownership of the command.
Thread #8 is about to execute the command on the connection with ID 11f558a0-128d-4401-94cb-57fff007ae81.
Thread #8 successfully executed the command.
Thread #8 is about to relinquish ownership of the command.
Thread #8 is going to sleep with open pooled connection.
Thread #9 has ownership of the command.
Thread #9 is about to execute the command on the connection with ID c613e347-1471-4476-a6d7-1485e2c731c5.
Thread #9 successfully executed the command.
Thread #9 is about to relinquish ownership of the command.
Thread #9 is going to sleep with open pooled connection.
Thread #0 has woken up and disposed of its pooled connection.
Thread #2 has woken up and disposed of its pooled connection.
Thread #1 has woken up and disposed of its pooled connection.
Thread #3 has woken up and disposed of its pooled connection.
Thread #4 has woken up and disposed of its pooled connection.
Thread #5 has woken up and disposed of its pooled connection.
Thread #6 has woken up and disposed of its pooled connection.
Thread #7 has woken up and disposed of its pooled connection.
Thread #8 has woken up and disposed of its pooled connection.
Thread #9 has woken up and disposed of its pooled connection.

所以基本上看起来没什么好担心的。我的老板 运行 在数据库服务器上有一个 SQL 服务器配置文件,而我是 运行 上面的测试程序,并声称他从未真正看到对 sp_prepare 的调用。考虑到 SQL 服务器缓存执行计划这一事实,这可能不是什么大问题。

不幸的是,自从写下我的问题后,我意识到 SqlConnection.EnlistTransaction 方法并不像我最初希望的那样有用(在我的特定情况下)。由于这个事实,看来我们必须将 t运行saction 作为我的应用程序中的特例来处理。