简体   繁体   English

NHibernate和ADO.NET连接池

[英]NHibernate and ADO.NET Connection Pooling

It seems that NHibernate does not pool ADO.NET database connections. 似乎NHibernate不会集合ADO.NET数据库连接。 Connections are only closed when the transaction is committed or rolled back. 仅在提交或回滚事务时关闭连接。 A review of the source code shows that there is no way to configure NHibernate so that it is closing connections when the ISession is disposed. 对源代码的回顾表明,没有办法配置NHibernate,以便在处理ISession时关闭连接。

What was the intent of this behaviour? 这种行为的意图是什么? ADO.NET has connection pooling itself. ADO.NET本身就有连接池。 There's no need to hold them open all the time within the transaction. 在交易中不需要一直打开它们。 With this behaviour are also unneccessaryly distributed transactions created. 使用此行为也是不必要的分布式事务创建。 A possible workaround described in http://davybrion.com/blog/2010/05/avoiding-leaking-connections-with-nhibernate-and-transactionscope/ therefore does not work (at least not with NHibernate 3.1.0). 因此, http: //davybrion.com/blog/2010/05/avoiding-leaking-connections-with-nhibernate-and-transactionscope/中描述的可能解决方法不起作用(至少不适用于NHibernate 3.1.0)。 I am using Informix. 我正在使用Informix。 The same problem seems to exisit for every other database ( NHibernate Connection Pooling ). 对于每个其他数据库( NHibernate Connection Pooling ),似乎存在同样的问题。

Is there any other workaround or advice avoiding this problem? 有没有其他解决方法或建议避免这个问题?

Here's a unit test reproducing the problem: 这是一个重现问题的单元测试:

  [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'
        }
     }
  }

The disposing of the NHibernate-Session does nothing since we are still in a transaction 处理NHibernate-Session没有任何作用,因为我们仍处于事务中

SessionImpl.cs: 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);
        }
    }

Injecting a custom ConnectionProvider will also not work since the ConnectionManager calling the ConnectionProvider has several preconditions checking that closing a connection within a transaction is not allowed. 注入自定义ConnectionProvider也不起作用,因为ConnectionManager调用ConnectionProvider有几个先决条件,检查不允许在事务中关闭连接。

ConnectionManager.cs: 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 has two "modes". NHibernate有两种“模式”。

  • Either you open the connection in your application, then it is up to the application to manage it. 您可以在应用程序中打开连接,然后由应用程序来管理它。 This "mode" is used when passing a connection to sessionfactory.OpenSession(connection) . 将连接传递给sessionfactory.OpenSession(connection)时使用此“模式”。
  • Or the connection is created by NH. 或者连接由NH创建。 Then it is closed when the session is closed. 然后在会话结束时关闭它。 This "mode" is used when not passing a connection to sessionfactory.OpenSession() 未传递连接到sessionfactory.OpenSession()时使用此“模式”

There is some support for TransactionScope . TransactionScope有一些支持。 It is most probably using the first "mode". 它最有可能使用第一个“模式”。 Probably the connection is not hold by NH, but by the transaction scope. 可能连接不是由NH持有,而是由交易范围。 I don't know exactly, I don't use environment transactions. 我不确切知道,我不使用环境事务。

NH is using the ADO.NET connection pool by the way. NH 正在使用ADO.NET连接池。

You can also disconnect the session using ISession.Disconnect() and reconnect using ISession.Reconnect() . 您也可以断开使用会话ISession.Disconnect()并使用重新ISession.Reconnect()

In the documentation you find: 文档中,您会发现:

The method ISession.Disconnect() will disconnect the session from the ADO.NET connection and return the connection to the pool (unless you provided the connection). 方法ISession.Disconnect()将断开会话与ADO.NET连接的连接,并将连接返回到池(除非您提供了连接)。

You can accomplish this by adding the following settings to your connection string. 您可以通过将以下设置添加到连接字符串来完成此操作。

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

Pooling: enables pooling for your app 池:为您的应用程序启用池

Min Pool: The minimum number of connections to keep open even when all sessions are closed. 最小池:即使所有会话都关闭也要保持打开的最小连接数。

Max Pool: The max number of connections the app will open to the DB. 最大池数:应用程序将向数据库打开的最大连接数。 When the max is reached it will wait for the number of seconds specified by Connection Timeout and then throw an exception. 达到最大值时,它将等待“连接超时”指定的秒数,然后抛出异常。

Connection Timeout: Maximum Time (in secs) to wait for a free connection from the pool 连接超时:等待池中的空闲连接的最长时间(以秒为单位)

Connection Lifetime: When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by Connection Lifetime. 连接生存期:将连接返回到池时,将其创建时间与当前时间进行比较,如果该时间跨度(以秒为单位)超过Connection Lifetime指定的值,则会破坏连接。 A value of zero (0) causes pooled connections to have the maximum connection timeout. 值为零(0)会导致池连接具有最大连接超时。

Incr Pool Size: Controls the number of connections that are established when all the connections are used. Incr Pool Size:控制使用所有连接时建立的连接数。

Decr Pool Size: Controls the number of connections that are closed when an excessive amount of established connections are unused. Decr池大小:控制在未使用过多已建立连接时关闭的连接数。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM