【问题标题】:does nhibernate create implicit transactions within a TransactionScope?nhibernate 会在 TransactionScope 中创建隐式事务吗?
【发布时间】:2009-12-30 18:36:15
【问题描述】:

我创建了一个集成测试来验证存储库是否正确处理并发。如果我在没有 TransactionScope 的情况下运行测试,一切都按预期工作,但是如果我将测试包装在 TransactionScope 中,我会收到一个错误,提示突然需要分布式事务(这让我相信还有第二个事务正在创建)。这是测试:

    [Test]
    public void Commit_ItemToCommitContainsStaleData_ThrowsStaleObjectStateException()
    {
        using (new TransactionScope())
        {
            // arrange
            RootUnitOfWorkFactory factory = CreateUnitOfWorkFactory();
            const int Id = 1;

            WorkItemRepository firstRepository = new WorkItemRepository(factory);
            WorkItem itemToChange = WorkItem.Create(Id);
            firstRepository.Commit(itemToChange);

            WorkItemRepository secondRepository = new WorkItemRepository(factory);
            WorkItem copyOfItemToChange = secondRepository.Get(Id);

            // act
            copyOfItemToChange.ChangeDescription("A");
            secondRepository.Commit(copyOfItemToChange);

            itemToChange.ChangeDescription("B");

            // assert
            Assert.Throws<StaleObjectStateException>(() => firstRepository.Commit(itemToChange));
        }
    }

这是错误堆栈的底部:

失败:NHibernate.Exceptions.GenericADOException:无法加载实体:[TfsTimeMachine.Domain.WorkItem#1][SQL:SELECT workitem0_.Id as Id1_0_,workitem0_.LastChanged as LastChan2_1_0_,workitem0_.Description as Descript3_1_0_ FROM [WorkItem ] workitem0_ WHERE workitem0_.Id=?] ----> System.Data.SqlClient.SqlException:服务器“ADM4200\SQLEXPRESS”上的 MSDTC 不可用。 在 NHibernate.Loader.Loader.LoadEntity(ISessionImplementor session, Object id, IType identifierType, Object optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister)。

我正在运行 NUnit 2.1,所以有人可以告诉我,如果在查询数据之前没有 session.BeginTransaction(),Nhibernate 是否会创建隐式事务,而不管在 TransactionScope 中运行的会话如何?

【问题讨论】:

    标签: nhibernate


    【解决方案1】:

    我得到了这个工作。问题是(如我的评论中所述)两个并发会话是在同一个事务范围内启动的,并且都启动了一个新的数据库连接,该数据库连接了相同的事务,从而迫使 DTC 启动。解决方案是创建一个自定义连接提供程序这确保了在事务范围内返回相同的连接。然后我在我的测试和 presto 中使用它,我可以测试陈旧的对象状态并在测试完成时回滚数据。这是我的实现:

    /// <summary>
    /// A connection provider which returns the same db connetion while
    /// there exists a TransactionScope.
    /// </summary>
    public sealed class AmbientTransactionAwareDriverConnectionProvider : IConnectionProvider
    {
        private readonly bool disposeDecoratedProviderWhenDisposingThis;
        private IConnectionProvider decoratedProvider;
        private IDbConnection maintainedConnectionThroughAmbientSession;
    
        public AmbientTransactionAwareDriverConnectionProvider()
            : this(new DriverConnectionProvider(), true)
        {}
    
        public AmbientTransactionAwareDriverConnectionProvider(IConnectionProvider decoratedProvider,
                                                          bool disposeDecoratedProviderWhenDisposingThis)
        {
            Guard.AssertNotNull(decoratedProvider, "decoratedProvider");
            this.decoratedProvider = decoratedProvider;
            this.disposeDecoratedProviderWhenDisposingThis = disposeDecoratedProviderWhenDisposingThis;
        }
    
        ~AmbientTransactionAwareDriverConnectionProvider()
        {
            Dispose(false);
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        public void Configure(IDictionary<string, string> settings)
        {
            this.decoratedProvider.Configure(settings);
        }
    
        public void CloseConnection(IDbConnection conn)
        {
            if (Transaction.Current == null)
                this.decoratedProvider.CloseConnection(conn);
        }
    
        public IDbConnection GetConnection()
        {
            if (Transaction.Current == null)
            {
                if (this.maintainedConnectionThroughAmbientSession != null)
                    this.maintainedConnectionThroughAmbientSession.Dispose();
    
                return this.decoratedProvider.GetConnection();
            }
    
            if (this.maintainedConnectionThroughAmbientSession == null)
                this.maintainedConnectionThroughAmbientSession = this.decoratedProvider.GetConnection();
    
            return this.maintainedConnectionThroughAmbientSession;
        }
    
        private void Dispose(bool disposing)
        {
            if (this.maintainedConnectionThroughAmbientSession != null)
                CloseConnection(this.maintainedConnectionThroughAmbientSession);
    
            if (this.disposeDecoratedProviderWhenDisposingThis && this.decoratedProvider != null)
                this.decoratedProvider.Dispose();
    
            if (disposing)
            {
                this.decoratedProvider = null;
                this.maintainedConnectionThroughAmbientSession = null;
            }
        }
    
        public IDriver Driver
        {
            get { return this.decoratedProvider.Driver; }
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      我不确定 Hibernate 是否在内部使用事务,但我也不认为这是你的问题。

      问题似乎在于您在同一事务中使用了两个不同的数据源。为了协调两个数据源之间的事务以进行两阶段提交,您需要启用 DTC。两个数据源实际上是同一个数据库这一事实并不重要。

      【讨论】:

      • 是的,你是对的。所以答案是否定的,Nhibernate 不会创建隐式事务。这会失败,因为两个不同的 ADO.net 连接登记到同一个事务,所以第二个存储库上的 get 请求会导致错误。现在我只需要控制事务范围内的会话连接(在我的情况下应该不需要 dist trans)来让它工作
      猜你喜欢
      • 1970-01-01
      • 2012-04-29
      • 1970-01-01
      • 1970-01-01
      • 2020-04-24
      • 1970-01-01
      • 1970-01-01
      • 2011-03-12
      • 1970-01-01
      相关资源
      最近更新 更多