2009-12-30 69 views
2

我创建了一个集成测试来验证存储库是否正确处理并发。如果我在没有TransactionScope的情况下运行测试,一切都按预期运行,但是如果我将测试包装在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上的MSDTC \ SQLEXPRESS'不可用。 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中运行的会话如何?

回答

1

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

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

+0

是的,你是正确的。所以答案是否定的,Nhibernate不会创建隐式事务。这会失败,因为两个不同的ADO.net连接会登录到同一事务,所以第二个存储库上的get请求会导致错误。现在我只需要控制一个交易范围内的会话连接(在我的情况下不需要远程转换)来使其工作 – Marius 2009-12-31 08:48:17

2

我得到了这个工作。问题在于(正如我的评论所述),两个并发会话是在同一个事务处理范围内开始的,并且都启动了一个新的dbconnection,这个dbconnection参与了相同的事务,因此迫使DTC开始工作。解决方案是创建一个自定义连接提供程序这确保了在transactioncope内部返回相同的连接。然后我在测试中发挥了这一作用,我可以测试过时的对象状态并在测试完成时回滚数据。继承人我的执行:

/// <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; } 
    } 

} 
相关问题