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

查看:78
本文介绍了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.

在我们的存储库基类中,我们总是根据

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屋!

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