2009-12-30 7 views
2

Я создал интеграционный тест, чтобы проверить, что репозиторий корректно обрабатывает параллелизм. Если я проведу тест без TransactionScope, все будет работать так, как ожидалось, но если я завершу тест в TransactionScope, я получу ошибку, предполагающую, что внезапная потребность в распределенных транзакциях (что заставило меня поверить, что есть вторая транзакция создается). Вот тест:ли nhibernate создает неявные транзакции в 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: MSDTC на сервере 'ADM4200 \ SQLEXPRESS 'недоступен. at NHibernate.Loader.Loader.LoadEntity (сеанс ISessionImplementor, идентификатор объекта, идентификатор ITypeType, объект optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister).

Я запускаю NUnit 2.1, так может кто-то сказать мне, если Nhibernate создает неявные транзакции, если перед запросом данных нет сеанса.BeginTransaction(), независимо от сеанса, выполняющегося в TransactionScope?

ответ

1

Я не уверен, что Hibernate использует транзакции внутренне, но я также не думаю, что это ваша проблема здесь.

Похоже, проблема состоит в том, что вы используете два разных источника данных в одной транзакции. Чтобы координировать транзакцию между обоими источниками данных для двухфазной фиксации, вам необходимо включить DTC. Тот факт, что оба источника данных являются фактически одной и той же базой данных, не имеет значения.

+0

Да, вы правы. Таким образом, ответ отрицательный, Nhibernate не создает неявных транзакций. Это не удается, потому что два разных соединения ADO.net присоединяются к одной и той же транзакции, поэтому запрос на получение во втором репозитории вызывает ошибку. Теперь мне просто нужно контролировать сеансовое соединение внутри транзакции (в моем случае не должно быть необходимости в dist trans), чтобы заставить это работать – Marius

2

Я получил это для работы. Проблема заключалась в том, что в одном и том же транзакционном сеансе были запущены две параллельные сессии, и оба начали новую dbconnection, которая заручилась той же транзакцией, что заставило DTC зайти. Решением этого было создание настраиваемого поставщика подключения что обеспечило, что такое же соединение было возвращено во время транзакции. Затем я включил это в свой тест и престо, я смог проверить состояние устаревшего объекта и отбросить данные, когда тесты завершатся. Heres моя реализация:

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

} 
Смежные вопросы