2010-08-20 96 views
2

我想与NHibernate使用TransactionScope为了在一个事务中调用几个方法。数据存储库的方法是这样的:NHibernate,TransactionScope和锁定

 
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()之前的旧值读取该行,然后再读取该值。我怎样才能实现与这个模型锁定?

回答

0

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

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

法拉盛完成(取决于冲洗模式,这通常是自动冲洗)

  • 查询之前(除获取和加载)
  • 打电话时冲洗明确
  • 提交事务时
  • (如果连接是由NH创建的我认为)

当会话刷新时,实际的更新,插入和删除操作在数据库上完成并设置锁。

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

+1

我有这个会话: session.FlushMode = FlushMode.Commit; 我知道实际更新是在提交时完成的,但是如果仅在执行数据库查询时完成锁定,则TransactionScope及其隔离级别的意义何在? 以下是有关隔离级别的SQL Server文档的链接: http://msdn.microsoft.com/en-us/library/aa259216(SQL.80).aspx 根据此,​​每个SELECT都在内部完成具有可重复读取隔离级别的事务应该锁定行,这在我的示例中不是这种情况。 – IvanQ 2010-08-20 10:26:37

+0

我也可能是嵌套事务的问题。我不使用这个事务范围 - 我写我自己的地方,我知道实际发生了什么... – 2010-08-20 11:49:27

+0

我想写我自己的地方我会抽象NHibernate会话和事务,但现在我写了一些测试,使用NHibernate事务和结果是一样的。仅在数据被写入时获取锁。 – IvanQ 2010-08-20 11:54:21