Пулы соединений NHibernate и ADO.NET - PullRequest
9 голосов
/ 27 сентября 2011

Кажется, что 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 Connection Pooling ).

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

Вот воспроизведение модульного тестапроблема:

  [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 (ISessionFactory 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 ]

9 голосов
/ 27 сентября 2011

NHibernate имеет два «режима».

  • Либо вы открываете соединение в своем приложении, но затем приложение может управлять им.Этот «режим» используется при передаче соединения на sessionfactory.OpenSession(connection).
  • или соединение создается NH.Затем он закрывается, когда сессия закрыта.Этот «режим» используется, когда соединение не передается на sessionfactory.OpenSession()

. Есть некоторая поддержка TransactionScope.Скорее всего, он использует первый «режим».Возможно, соединение удерживается не NH, а областью транзакции.Я точно не знаю, я не использую транзакции среды.

NH - это , кстати, используя пул соединений ADO.NET.

Вы также можете отключитьсеанс с использованием ISession.Disconnect() и переподключение с использованием ISession.Reconnect().

В документации вы найдете:

Метод ISession.Disconnect () отключит сеанс от соединения ADO.NET и вернет соединение кпул (если вы не предоставили соединение).

0 голосов
/ 19 мая 2016

Это можно сделать, добавив следующие параметры в строку подключения.

Pooling=true; 
Min Pool Size=3;
Max Pool Size=25;
Connection Lifetime=7200;
Connection Timeout=15;
Incr Pool Size=3;
Decr Pool Size=5;

Пул: включает пул для вашего приложения

Мин. Пул: минимальное количество соединений, которые остаются открытыми.даже когда все сеансы закрыты.

Макс. пул: максимальное количество соединений, которые приложение откроет к БД.При достижении максимума он будет ждать количество секунд, указанное в Тайм-ауте соединения, а затем выдает исключение.

Тайм-аут соединения: Максимальное время (в секундах) ожидания свободного соединения из пула

Время жизни соединения: когда соединение возвращается в пул, время его создания сравнивается с текущим временем, и соединение разрывается, если этот промежуток времени (в секундах) превышает значение, указанное в времени жизни соединения.Нулевое значение (0) приводит к тому, что для пулированных подключений максимальный тайм-аут соединения.

Incr Pool Size: Управляет количеством соединений, которые устанавливаются при использовании всех соединений.

Decr PoolРазмер: Управляет количеством соединений, которые закрываются, если чрезмерное количество установленных соединений не используется.

http://www.codeproject.com/Articles/17768/ADO-NET-Connection-Pooling-at-a-Glance

...