看来,NHibernate不集中ADO.NET数据库连接。连接只在事务提交或回滚时关闭。对源代码的回顾显示,没有办法配置NHibernate,以便在ISession处置时关闭连接。NHibernate和ADO.NET连接池
这种行为的意图是什么? 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的会话的载放什么也不做,因为我们仍然在交易
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);
}
}
}
据我所知,为了利用事务,数据库需要相同的连接。所以我不觉得奇怪的是,只要事务正在运行,它就会保持连接正常运行?如果连接返回到池中,则不会确保您第二次从池中接收到相同的连接。 – jishi
但是,在您的具体测试中,您正在检查基础IdbConnection,我认为它是ADO.NET的一部分,并且不是您在此情况下测试的ADO.NET连接池吗?你应该做的是创建两个不同的会话(从同一工厂,以确保是这种情况),并确保您收到相同的连接。 – jishi
在每个事务开始时,Driver类(在我的情况下是OdbcDriver)创建一个新的DbConnection(OdbcConnection)。这个连接保持打开整个事务是不必要的。我写的测试实际上使用了一个SessionFactory中的两个不同的会话。 – Antineutrino