【问题标题】:NHibernate, TransactionScope and lockingNHibernate、TransactionScope 和锁定
【发布时间】:2010-08-20 09:55:52
【问题描述】:

我正在尝试将 TransactionScope 与 NHibernate 一起使用,以便在一个事务中调用多个方法。数据存储方法是这样的:

public virtual void Save(T dataObject)
{
    try
    {
        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }))
        {
            this.session.SaveOrUpdate(dataObject);
            scope.Complete();
        }
    }
    catch (Exception ex)
    {
        bool rethrow = ExceptionPolicy.HandleException(ex, "Data Layer Policy");
        if (rethrow)
        {
            throw;
        }
    }
}

public T GetByNumber(string documentNumber) { T文档=空; 尝试 { 使用 (TransactionScope 范围 = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead })) { 文档 = this.Session.CreateCriteria(typeof(T)) .Add(Restrictions.Eq("Number", documentNumber)) .UniqueResult(); 范围。完成(); } } 捕捉(例外前) { bool rethrow = ExceptionPolicy.HandleException(ex, "Data Layer Policy"); 如果(重新抛出) { 扔; } } 退货文件; }

我想测试事务中的行/表锁定,所以我做了几个单元测试和一些控制台应用程序。以下是这些控制台应用程序的代码:

更新的应用程序:

const string DocumentNumber = "386774321";
Random randomGenerator = new Random();
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }))
{
    using (BillingDocumentRepository billingDocumentRepository = new BillingDocumentRepository())
    {
        BillingOrderData orderData = billingDocumentRepository.GetByNumber(DocumentNumber);
        orderData.Notes = randomGenerator.Next().ToString();
        Console.WriteLine(string.Format("SECOND: {0}: Updated notes to {1}.", DateTime.Now.ToString("HH:mm:ss.fffff"), orderData.Notes));
        Console.WriteLine(string.Format("SECOND: {0}: Updating order.", DateTime.Now.ToString("HH:mm:ss.fffff")));
        Console.WriteLine(string.Format("SECOND: {0}: Going to sleep for 10000ms.", DateTime.Now.ToString("HH:mm:ss.fffff")));
        Sleep(10000); // My custom sleep method because I didn't want to use Thread.Sleep for simulating long transaction
        billingDocumentRepository.Save(orderData);
    }
    Console.WriteLine(string.Format("SECOND: {0}: Going to sleep for  10000ms.", DateTime.Now.ToString("HH:mm:ss.fffff")));
    Sleep(10000);
    Console.WriteLine(string.Format("SECOND: {0}: Completing transaction.", DateTime.Now.ToString("HH:mm:ss.fffff")));
    scope.Complete();
}

读取数据库中同一行的应用程序:

while (true)
{
    using (BillingDocumentRepository repository = new BillingDocumentRepository())
    {
        Console.WriteLine(string.Format("MAIN: {0}: Getting document.", DateTime.Now.ToString("HH:mm:ss.fffff")));
        BillingOrderData billingOrderData = repository.GetByNumber("386774321");
        Console.WriteLine(string.Format("MAIN: {0}: Got order with notes {1}.", DateTime.Now.ToString("HH:mm:ss.fffff"), billingOrderData.Notes));
        Sleep(1000);
    }
}

问题是第一个事务(更新行)在任何时候都不会锁定行以供读取。第二个应用程序一直在使用 scope.Complete() 之前的旧值和之后的新值读取该行。如何使用此模型实现锁定?

【问题讨论】:

    标签: nhibernate transactions locking transactionscope


    【解决方案1】:

    阅读时应锁定。稍后锁定“为时已晚”:

     document = this.Session.CreateCriteria(typeof(T))
                .Add(Restrictions.Eq("Number", documentNumber))
                .SetLockMode(LockMode.Upgrade)
                .SetTimeout(5)
                .UniqueResult();
    

    或者:

     var doc = session.QueryOver<BillingDocument>()
                   .Where(c => c.Number== "2233445")
                    .Lock()
                    .Upgrade
                    .UnderlyingCriteria.
                     SetTimeout(5).
                     List().
                     FirstOrNull() as BillingDocument;
    

    【讨论】:

      【解决方案2】:

      有一个session.Lock(object) 方法。

      当您调用session.Save(object) 时,NHibernate 不会在数据库中执行任何操作,直到它被刷新。

      刷新完成(取决于刷新模式,通常是 AutoFlush)

      • 查询前(Get 和 Load 除外)
      • 显式调用刷新时
      • 提交事务时(如果我认为连接是由 NH 创建的)

      当刷新会话时,对数据库进行实际的更新、插入和删除操作并设置锁。

      在 SQL Server 中,当设置了锁时,读取事务一直在等待更新事务的提交。当它提交时,它会读取已提交的值(当您处于“Read Committed”隔离状态时)。

      【讨论】:

      • 我有这个用于会话: session.FlushMode = FlushMode.Commit;我知道实际更新是在提交时完成的,但是如果仅在执行数据库查询时才进行锁定,那么 TransactionScope 及其隔离级别的意义何在?这是 SQL Server 关于隔离级别的文档的链接:msdn.microsoft.com/en-us/library/aa259216(SQL.80).aspx 据此,在具有可重复读取隔离级别的事务内完成的每个 SELECT 都应该锁定行,这在我的示例中不是这种情况。
      • 我也可能是嵌套事务的问题。我不使用这个事务范围——我自己写的,我知道实际发生了什么......
      • 我正在考虑编写自己的代码来抽象 NHibernate 会话和事务,但现在我编写了一些使用 NHibernate 事务的测试,结果是相同的。只有在写入数据时才会获取锁。
      • 更新实际上是在提交写入事务时完成的。这是20秒后。当你调用最终的 scope.Complete 是假设。以前,没有任何锁。
      • 是的,我知道,但第一次选择也是在事务内部完成的,它应该是某种 SELECT FOR UPDATE 类型。这就是将事务用于整个处理的目的,而不仅仅是用于保存。如果其他线程做同样的事情怎么办?我会丢失更新问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-08
      相关资源
      最近更新 更多