看來,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