我正在尝试将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()之前一直使用旧值读取该行,然后是新值。如何使用此模型实现锁定?
答案 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)
刷新会话时,会对数据库执行实际的更新,插入和删除操作,并设置锁定。
在SQL Server中,当设置了锁定时,读取事务将一直等到更新事务的提交。提交时,它会读取已提交的值(当您处于“Read Committed”隔离时)。