Dot-Net

NHibernate 和 ADO.NET 連接池

  • May 19, 2016

似乎 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 也將不起作用,因為呼叫 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);
           }
       }
   }

NHibernate 有兩種“模式”。

  • 您可以在應用程序中打開連接,然後由應用程序來管理它。此“模式”用於將連接傳遞到sessionfactory.OpenSession(connection).
  • 或者連接是由 NH 創建的。然後在會話關閉時關閉。此“模式”在不傳遞連接時使用sessionfactory.OpenSession()

有一些支持TransactionScope。它最有可能使用第一個“模式”。可能連接不是由 NH 持有,而是由事務範圍持有。我不確切知道,我不使用環境事務。

順便說一下, NH正在使用 ADO.NET 連接池。

您還可以使用 斷開會話ISession.Disconnect()並使用 重新連接ISession.Reconnect()

在您找到的文件中:

方法 ISession.Disconnect() 將斷開會話與 ADO.NET 連接並將連接返回到池(除非您提供了連接)。

引用自:https://stackoverflow.com/questions/7567233