在现有的SqlConnection using块内使用new SqlConnection创建一个事务范围

3

我希望执行一条SELECT查询,然后是一系列UPDATE查询(都在同一张表上); UPDATE实现在一个单独的方法中,该方法被重复调用。如果其中一个UPDATE查询失败,则我希望它们全部失败/回滚-因此我想将它们列入事务。但是,我不确定应该在哪里打开SqlConnection以避免任何问题。我的当前实现如下:

using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();

    // execute a single SELECT here

    using (TransactionScope scope = new TransactionScope())
    {
        for (int i=0; i<...; i++)
        {
            Update(); // UPDATE query
        }

        scope.Complete();
    }
}


Update()
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();

        // execute a single UPDATE here
    }
}

这在所有情况下都能正常工作吗?
在执行SELECT之前打开连接,然后在Update()方法中重新打开新连接,这样做可以吗?由于连接池的缘故,相同的连接将用于SELECT和UPDATE查询(connectionString是相同的),但只有UPDATE查询会被注册到事务中,对吗?但如果在Update()中使用不同的连接会发生什么?所有UPDATE查询是否仍然按预期注册到事务中并以原子方式执行?
如果我理解正确,在关闭第一个连接之后(在执行SELECT的using块之后)创建事务范围仍然有效,但会降低性能,因为连接将被关闭并需要重新打开,对吗?或者实际上,每次调用Update()时都会为事务范围创建一个新连接,并在其中打开和关闭?
1个回答

8
由于连接池技术的使用,同一个连接会同时被用于SELECT和UPDATE查询。
不,它不会这样做;除非您首先将连接释放回池中 - 目前您有两个并发连接,因此它不可能是同一件事。 您需要进行重构:
using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();

    // execute a single SELECT here
} // <==== end the first connection

using (TransactionScope scope = new TransactionScope())
{
    for (int i=0; i<...; i++)
    {
        Update(); // UPDATE query
    }

    scope.Complete();
}

在关闭第一个连接后创建事务范围(...)会降低性能,因为需要重新打开连接,正确吗?
不正确;当您“关闭”它时,您实际上只是将其释放回池中;它不会关闭底层连接(除非禁用了池)。关闭(或至少处置)是正常且预期的。
但只有UPDATE查询会在事务中注册,对吧?
正确,因为那是事务范围内打开连接的唯一位置。
但是如果在Update()中使用了不同的连接会怎样?所有UPDATE查询仍然会像预期的那样注册到事务中并原子执行吗?
任何支持注册的连接(假设在连接字符串中没有禁用)都将被注册。但是,根据服务器的不同,这可能使用LTM或DTC。如果您想确保自己的连接,请控制它们:
using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();

    // execute a single SELECT here
} // <==== end the first connection

using (TransactionScope scope = new TransactionScope())
using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    for (int i=0; i<...; i++)
    {
        Update(connection); // UPDATE query
    }

    scope.Complete();
}

请注意,上面的代码中我将 connection 作为参数传递给了 Update;显然,只使用一个连接(假设 Update 的工作正常)。


1
@w128 注意,我进行了几次编辑,请确保您已经刷新以查看最新版本。 - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接