NHibernate,事务和TransactionScope

Eri*_*röm 5 nhibernate session transactions transactionscope

我正在尝试找到在使用NHibernate的Web应用程序中处理事务的最佳解决方案.

我们使用IHttpModule,在HttpApplication.BeginRequest中我们打开一个新会话,然后用ManagedWebSessionContext.Bind(context,session)将它绑定到HttpContext; 我们关闭并取消绑定HttpApplication.EndRequest上的会话.

在我们的Repository基类中,我们总是围绕我们的SaveOrUpdate,Delete,Get方法包装一个事务,例如,根据最佳实践:

        public virtual void Save(T entity)
        {
          var session = DependencyManager.Resolve<ISession>();
          using (var transaction = session.BeginTransaction())
          {
            session.SaveOrUpdate(entity);
            transaction.Commit();
          }
        }
Run Code Online (Sandbox Code Playgroud)

但是,如果您需要在某个应用程序服务中的某个位置放置一个事务来包含多个存储库调用以保存,删除等,则这不起作用.

所以我们尝试使用TransactionScope(我不想编写自己的事务管理器).为了测试这是否有效,我使用外部TransactionScope,它不会调用.Complete()来强制回滚:

存储库保存():

    public virtual void Save(T entity)
    {
        using (TransactionScope scope = new TransactionScope())
        {
            var session = DependencyManager.Resolve<ISession>();
            session.SaveOrUpdate(entity);
            scope.Complete();
        }   
    }  
Run Code Online (Sandbox Code Playgroud)

使用存储库的块:

        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"));
Run Code Online (Sandbox Code Playgroud)

这不起作用.但对我来说,如果NHibernate支持TransactionScope,它会!发生的事情是数据库中根本没有ROLLBACK但是当testRepository.GetById(testEntity.Id); 语句执行UPDATE与SET Text ="TestCahgned"被触发(它应该在BEGIN TRAN和ROLLBACK TRAN之间被触发).NHibernate从level1缓存读取值并向数据库发出UPDATE.不期望的行为!?根据我的理解,只要在NHibernate的范围内完成回滚,您还需要关闭和取消绑定当前会话.

我的问题是:有没有人知道使用TransactionScope和ManagedWebSessionContext做到这一点的好方法?

Tor*_*ups 2

我采取了非常相似的方法。在 HttpModule 中,我向 sessionfactory 请求一个新会话并在新请求传入时绑定它。但我也在此处开始事务。然后,当请求结束时,我只需解除绑定并尝试提交事务。

此外,我的基本存储库不会以任何方式获取会话 - 它会请求当前会话,然后使用该会话执行一些工作。另外,我不会用事务将任何内容包装在这个基类中。相反,整个 http 请求是一个工作单元。

这可能不适合您正在处理的项目,但我更喜欢这种方法,因为每个请求都会作为单个原子单元失败或成功。如果您对实际实现感兴趣, 我这里有一篇完整的博客文章,其中包含源代码。

下面是这个基础存储库的示例:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)