2009-12-30 68 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; } 
    } 

} 
相關問題