Пулы соединений NHibernate и ADO.NET

Кажется, что NHibernate не объединяет соединения с базой данных ADO.NET. Соединения закрываются, только когда транзакция зафиксирована или откатана. Обзор исходного кода показывает, что нет способа настроить NHibernate, чтобы он закрывал соединения при утилизации ISession.

Какова была цель этого поведения? ADO.NET имеет сам пул соединений. Нет необходимости держать их открытыми все время внутри транзакции. При таком поведении также создаются ненужно распределенные транзакции. Возможный обходной путь описан вhttp://davybrion.com/blog/2010/05/avoiding-leaking-connections-with-nhibernate-and-transactionscope/ поэтому не работает (по крайней мере, не с NHibernate 3.1.0). Я использую Informix. Такая же проблема, похоже, существует для любой другой базы данных (Пул соединений NHibernate).

Есть ли другой способ или совет, чтобы избежать этой проблемы?

Вот модульный тест, воспроизводящий проблему:

  [Test]
  public void DoesNotCloseConnection()
  {
     using (SessionFactoryCache sessionFactoryCache = new SessionFactoryCache())
     {
        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromMinutes(10) }))
        {
           fixture.Setup(); // Creates test data

           System.Data.IDbConnection connectionOne;
           System.Data.IDbConnection connectionTwo;

           using (ISession,Factory sessionFactory = sessionFactoryCache.CreateFactory(GetType(), new TestNHibernateConfigurator()))
           {
              using (ISession session = sessionFactory.OpenSession())
              {
                 var result = session.QueryOver<Library>().List<Library>();
                 connectionOne = session.Connection;
              }
           }

           // At this point the first IDbConnection used internally by NHibernate should be closed

           using (ISessionFactory sessionFactory = sessionFactoryCache.CreateFactory(GetType(), new TestNHibernateConfigurator()))
           {
              using (ISession session = sessionFactory.OpenSession())
              {
                 var result = session.QueryOver<Library>().List<Library>();
                 connectionTwo = session.Connection;
              }
           }

           // At this point the second IDbConnection used internally by NHibernate should be closed

           // Now two connections are open because the transaction is still running
           Assert.That(connectionOne.State, Is.EqualTo(System.Data.ConnectionState.Closed)); // Fails because State is still 'Open'
           Assert.That(connectionTwo.State, Is.EqualTo(System.Data.ConnectionState.Closed)); // Fails because State is still 'Open'
        }
     }
  }

Утилизация NHibernate-Session ничего не делает, так как мы все еще находимся в транзакции

SessionImpl.cs:

public void Dispose()
    {
        using (new SessionIdLoggingContext(SessionId))
        {
            log.Debug(string.Format("[session-id={0}] running ISession.Dispose()", SessionId));
            if (TransactionContext!=null)
            {
                TransactionContext.ShouldCloseSessionOnDistributedTransactionCompleted = true;
                return;
            }
            Dispose(true);
        }
    }

Добавление пользовательского ConnectionProvider также не будет работать, так как ConnectionManager, вызывающий ConnectionProvider, имеет несколько предварительных условий, проверяющих, что закрытие соединения в транзакции недопустимо.

ConnectionManager.cs:

public IDbConnection Disconnect() {
        if (IsInActiveTransaction)
            throw  new InvalidOperationException("Disconnect cannot be called while a transaction is in progress.");

        try
        {
            if (!ownConnection)
            {
                return DisconnectSuppliedConnection();
            }
            else
            {
                DisconnectOwnConnection();
                ownConnection = false;
                return null;
            }
        }
        finally
        {
            // Ensure that AfterTransactionCompletion gets called since
            // it takes care of the locks and cache.
            if (!IsInActiveTransaction)
            {
                // We don't know the state of the transaction
                session.AfterTransactionCompletion(false, null);
            }
        }
    }

Ответы на вопрос(2)

Ваш ответ на вопрос