我想与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()之前的旧值读取该行,然后再读取该值。我怎样才能实现与这个模型锁定?
我有这个会话: 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
我也可能是嵌套事务的问题。我不使用这个事务范围 - 我写我自己的地方,我知道实际发生了什么... – 2010-08-20 11:49:27
我想写我自己的地方我会抽象NHibernate会话和事务,但现在我写了一些测试,使用NHibernate事务和结果是一样的。仅在数据被写入时获取锁。 – IvanQ 2010-08-20 11:54:21