Кажется, что 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 также не будет работать, так как 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);
}
}
}