NHibernate,TransactionScope和锁定

时间:2010-08-20 09:55:53

标签: nhibernate transactions locking transactionscope

我正在尝试将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 document = null; try { using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead })) { document = this.Session.CreateCriteria(typeof(T)) .Add(Restrictions.Eq("Number", documentNumber)) .UniqueResult(); scope.Complete(); } } catch (Exception ex) { bool rethrow = ExceptionPolicy.HandleException(ex, "Data Layer Policy"); if (rethrow) { throw; } } return document; }

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

更新的应用程序:

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()之前一直使用旧值读取该行,然后是新值。如何使用此模型实现锁定?

2 个答案:

答案 0 :(得分: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;

答案 1 :(得分:0)

session.Lock(object)方法。

当你调用session.Save(object)时,NHibernate在刷新之前没有在数据库中做任何事情。

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

    在查询之前
  • (获取和加载除外)
  • 明确调用flush时
  • 提交事务时(如果连接是由NH创建的,我认为)

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

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