NHibernate,事务和TransactionScope [英] NHibernate, transactions and TransactionScope
问题描述
我正在尝试找到最佳解决方案来处理使用NHibernate的Web应用程序中的事务.
I'm trying to find the best solution to handle transaction in a web application that uses NHibernate.
我们使用IHttpModule并在HttpApplication.BeginRequest处打开一个新会话,并使用ManagedWebSessionContext.Bind(context,session)将其绑定到HttpContext.我们关闭并取消在HttpApplication.EndRequest上的会话.
We use a IHttpModule and at HttpApplication.BeginRequest we open a new session and we bind it to the HttpContext with ManagedWebSessionContext.Bind(context, session); We close and unbind the session on HttpApplication.EndRequest.
In our Repository base class, we always wrapped a transaction around our SaveOrUpdate, Delete, Get methods like, according to best practice:
public virtual void Save(T entity)
{
var session = DependencyManager.Resolve<ISession>();
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(entity);
transaction.Commit();
}
}
但是如果您需要在某处进行交易,则此操作将无效.一个应用程序服务,其中包括对保存,删除等的多个存储库调用.
But then this doesn't work, if you need to put a transaction somewhere in e.g. a Application service to include several repository calls to Save, Delete, etc..
所以我们尝试的是使用TransactionScope(我不想编写自己的transactionmanager).为了测试它是否有效,我使用了一个不调用.Complete()来强制回滚的外部TransactionScope:
So what we tried is to use TransactionScope (I didn't want to write my own transactionmanager). To test that this worked, I use an outer TransactionScope that doesn't call .Complete() to force a rollback:
存储库Save():
public virtual void Save(T entity)
{
using (TransactionScope scope = new TransactionScope())
{
var session = DependencyManager.Resolve<ISession>();
session.SaveOrUpdate(entity);
scope.Complete();
}
}
使用存储库的块:
TestEntity testEntity = new TestEntity { Text = "Test1" };
ITestRepository testRepository = DependencyManager.Resolve<ITestRepository>();
testRepository.Save(testEntity);
using (var scope = new TransactionScope())
{
TestEntity entityToChange = testRepository.GetById(testEntity.Id);
entityToChange.Text = "TestChanged";
testRepository.Save(entityToChange);
}
TestEntity entityChanged = testRepository.GetById(testEntity.Id);
Assert.That(entityChanged.Text, Is.EqualTo("Test1"));
这不起作用.但是对我来说,如果NHibernate支持TransactionScope,它将!发生的情况是,在数据库中根本没有ROLLBACK,但是当testRepository.GetById(testEntity.Id);语句执行时执行SET文本="TestCahgned"的UPDATE代替(应在BEGIN TRAN和ROLLBACK TRAN之间触发). NHibernate从level1缓存中读取值,并将UPDATE触发到数据库.意外的行为!?据我了解,每当在NHibernate范围内完成回滚时,您还需要关闭并解除当前会话的绑定.
This doesn't work. But to me if NHibernate supports TransactionScope it would! What happens is that there is no ROLLBACK at all in the database but when the testRepository.GetById(testEntity.Id); statement is executed a UPDATE with SET Text = "TestCahgned" is fired instead (It should have been fired between BEGIN TRAN and ROLLBACK TRAN). NHibernate reads the value from the level1 cache and fires a UPDATE to the database. Not expected behaviour!? From what I understand whenever a rollback is done in the scope of NHibernate you also need to close and unbind the current session.
我的问题是:有人知道使用TransactionScope和ManagedWebSessionContext做到这一点的好方法吗?
My question is: Does anyone know of a good way to do this using TransactionScope and ManagedWebSessionContext?
推荐答案
我采用了非常相似的方法.在HttpModule中,我要求sessionfactory进行新的会话,并在收到新请求时将其绑定.但是我也从这里开始事务.然后,当请求结束时,我简单地将其解除绑定并尝试提交事务.
I took a very similar approach. In the HttpModule I ask the sessionfactory for a new session + bind it when a new request comes in. But I also begin the transaction here. Then when the request is ending I simply unbind it and attempt to commit the transaction.
我的基本存储库也不以任何方式进行会话-而是要求当前会话,然后对该会话执行一些工作.另外,我不会通过事务将任何东西包装在该基类中.相反,整个http请求是一个工作单元.
Also my base repository doesn't take a session in any way - it instead will ask for the current session and then perform some work with the session. Also I don't wrap anything inside this base class with a transaction. Instead the entire http request is a single unit of work.
这可能不适用于您正在处理的项目,但是我更喜欢这种方法,因为每个请求将作为单个原子单元失败或成功.我在此处中有完整的博客文章源代码(如果您对实际实现感兴趣).
This might not be appropriate for the project you are working on, but I prefer this approach because each request will fail or succeed as a single atomic unit. I have a full blog post here with source code if you are interested in the actual implementation.
以下是该基本存储库的示例:
The below is a sample of what this base repository looks like:
public abstract class NHibernateRepository<T> where T : class
{
protected readonly ISessionBuilder mSessionBuilder;
public NHibernateRepository()
{
mSessionBuilder = SessionBuilderFactory.CurrentBuilder;
}
public T Retrieve(int id)
{
ISession session = GetSession();
return session.Get<T>(id);
}
public void Save(T entity)
{
ISession session = GetSession();
session.SaveOrUpdate(entity);
}
public void Delete(T entity)
{
ISession session = GetSession();
session.Delete(entity);
}
public IQueryable<T> RetrieveAll()
{
ISession session = GetSession();
var query = from Item in session.Linq<T>() select Item;
return query;
}
protected virtual ISession GetSession()
{
return mSessionBuilder.CurrentSession;
}
}
这篇关于NHibernate,事务和TransactionScope的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!