NHibernate和ADO.NET连接池 [英] NHibernate and ADO.NET Connection Pooling
问题描述
似乎NHibernate不合并ADO.NET数据库连接.仅在提交或回滚事务时关闭连接.对源代码的回顾表明,没有方法可以配置NHibernate,以便在处置ISession时关闭NHibernate.
It seems that NHibernate does not pool ADO.NET database connections. 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.
此行为的目的是什么? ADO.NET本身具有连接池.无需在交易中一直将它们保持打开状态.通过这种行为,也不必要地创建了分布式事务. http://中描述的可能的解决方法davybrion.com/blog/2010/05/avoiding-leaking-connections-with-nhibernate-and-transactionscope/因此不起作用(至少不适用于NHibernate 3.1.0).我正在使用Informix.其他所有数据库似乎都存在相同的问题( NHibernate连接池).
What was the intent of this behaviour? ADO.NET has connection pooling itself. 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). I am using Informix. The same problem seems to exisit for every other database (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'
}
}
}
NHibernate-Session的处理无济于事,因为我们仍在交易中
The disposing of the NHibernate-Session does nothing since we are still in a transaction
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);
}
}
注入自定义ConnectionProvider也不起作用,因为调用ConnectionProvider的ConnectionManager具有几个前提条件,即检查不允许在事务中关闭连接.
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.
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具有两个模式".
NHibernate has two "modes".
- 您可以在应用程序中打开连接,然后由应用程序来管理它.将连接传递到
sessionfactory.OpenSession(connection)
时使用此模式". - 或者连接是由NH创建的.然后,当会话关闭时,它关闭.不将连接传递到
sessionfactory.OpenSession()
时使用此模式"
- 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)
. - Or the connection is created by NH. Then it is closed when the session is closed. This "mode" is used when not passing a connection to
sessionfactory.OpenSession()
有一些对TransactionScope
的支持.它最有可能使用第一个模式".可能不是NH保持连接,而是事务范围保持连接.我不知道,我不使用环境交易.
There is some support for TransactionScope
. It is most probably using the first "mode". Probably the connection is not hold by NH, but by the transaction scope. I don't know exactly, I don't use environment transactions.
NH 是使用ADO.NET连接池.
NH is using the ADO.NET connection pool by the way.
您还可以使用ISession.Disconnect()
断开会话并使用ISession.Reconnect()
重新连接.
You can also disconnect the session using ISession.Disconnect()
and reconnect using ISession.Reconnect()
.