14

我们在使用 TransactionScope 时遇到问题。TransactionScope 为我们提供了很好的灵活性,可以在我们的数据访问层中使用事务。通过这种方式,我们可以使用隐式或显式事务。再次 ADO.NET 事务有一些性能提升,但此时这并不是真正的问题。但是我们有锁定问题。在下面的示例代码中,虽然隔离级别设置为 ReadCommitted,但无法从其他客户端对表 testTable 进行 Select SQL 语句,直到主事务(在 Main 方法中)将被提交,因为整个表都有锁。我们还尝试在所有方法中仅使用一个连接,但行为相同。我们的 DBMS 是 SQL Server 2008。有什么我们不明白的地方吗?

问候安东卡尔奇克

请参阅此示例代码:

class Program
{
    public class DAL
    {
        private const string _connectionString = @"Data Source=localhost\fsdf;Initial Catalog=fasdfsa;Integrated Security=SSPI;";

        private const string inserttStr = @"INSERT INTO dbo.testTable (test) VALUES(@test);";

        /// <summary>
        /// Execute command on DBMS.
        /// </summary>
        /// <param name="command">Command to execute.</param>
        private void ExecuteNonQuery(IDbCommand command)
        {
            if (command == null)
                throw new ArgumentNullException("Parameter 'command' can't be null!");

            using (IDbConnection connection = new SqlConnection(_connectionString))
            {
                command.Connection = connection;
                connection.Open();
                command.ExecuteNonQuery();
            }
        }

        public void FirstMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello1"));

            using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required))
            {
                ExecuteNonQuery(command);
                sc.Complete();
            }
        }

        public void SecondMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello2"));

            using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required))
            {
                ExecuteNonQuery(command);
                sc.Complete();
            }
        }
    }

    static void Main(string[] args)
    {

        DAL dal = new DAL();
        TransactionOptions tso = new TransactionOptions();
        tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;

        using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required,tso))
        {
            dal.FirstMethod();
            dal.SecondMethod();
            sc.Complete();
        }
    }
}
4

2 回答 2

19

我认为您的问题与 .NET TransactionScope 概念没有任何关系。相反,听起来您在描述 SQL Server 事务的预期行为。此外,更改隔离级别只会影响“数据读取”而不是“数据写入”。从 SQL Server BOL:

“选择事务隔离级别不会影响为保护数据修改而获取的锁。事务总是在它修改的任何数据上获得排他锁,并持有该锁直到事务完成,无论为该事务设置的隔离级别如何。对于读操作,事务隔离级别主要定义了免受其他事务修改影响的保护级别。”

这意味着您可以通过更改发出语句的客户端的隔离级别来防止阻塞行为。SELECT隔离级别(READ COMMITED默认)不会阻止阻塞。为防止阻塞客户端,您将使用READ UNCOMMITTED隔离级别,但您必须考虑可能会检索到已由打开的事务更新/插入的记录(即,如果事务回滚,它们可能会消失)。

于 2009-04-06T19:32:11.777 回答
9

谈论交易的好问题。

您的主要方法是保持事务提交。即使您在其他方法中提交,您仍然会在该行上锁定。在您提交锁定事务之前,您将无法使用 READ COMMITTED 读取该表,这是预期的。

这是第一个方法返回后:

第一个方法返回

第二种方法返回后,您将为表添加一个锁。

第二个方法返回

如果我们从带有 SPID(55) 的查询窗口执行 select 语句,您将看到等待状态。

选择正在等待

在您的 main 方法 trans 提交后,您将获得 select 语句结果,它只会显示我们的 select 语句查询页面中的共享锁。

范围提交和选择返回

X表示排他锁,IX意向锁。您可以从我的博客文章中了解更多关于交易的信息

如果您想立即阅读,可以使用 nolock 提示。如果您想在第一个方法提交后阅读,您可以删除该外部范围。

于 2012-04-18T15:12:57.630 回答