NHibernate、事务和TransactionScope [英] NHibernate, transactions and TransactionScope

查看:20
本文介绍了NHibernate、事务和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.

在我们的 Repository 基类中,根据 最佳实践:

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(我不想编写自己的事务管理器).为了测试这是否有效,我使用了一个不调用 .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:

存储库保存():

    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 Text = TestCahgned"的更新;而是触发(它应该在 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆