NHibernate et ADO.NET le Regroupement de Connexion

Il semble que NHibernate n'a pas de piscine ADO.NET connexions de base de données. Les connexions ne sont fermé lorsque la transaction est validée ou annulée. Un examen du code source montre qu'il n'y a aucun moyen de le configurer NHibernate, de sorte que c'est la fermeture de la connexion lorsque le ISession est éliminé.

Ce qui était le but de ce comportement? ADO.NET a pool de connexion de lui-même. Il n'y a pas besoin de les tenir ouverts tout le temps dans la transaction. Ces comportements sont également unneccessaryly transactions distribuées créé. Une possible solution de contournement décrite dans http://davybrion.com/blog/2010/05/avoiding-leaking-connections-with-nhibernate-and-transactionscope/ donc ne fonctionne pas (du moins pas avec NHibernate 3.1.0). Je suis à l'aide de Informix. Le même problème semble exisit pour chaque autre base de données (NHibernate Le Regroupement De Connexion).

Est-il une autre solution ou des conseils en évitant ce problème?

Voici un test de l'unité de reproduire le problème:

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

L'élimination de la NHibernate-Session ne fait rien puisque nous sommes encore dans une transaction

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);
}
}

L'injection d'un personnalisé ConnectionProvider ne fonctionnera pas car le ConnectionManager l'appel de la ConnectionProvider a plusieurs conditions préalables de vérifier que la fermeture d'une connexion au sein d'une transaction n'est pas autorisée.

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);
}
}
}
Autant que je sache, les bases de données ont besoin de la même connexion pour utiliser une transaction. Donc, je ne trouve pas ça étrange qu'elle garde une connexion en vie aussi longtemps qu'une transaction est en cours d'exécution? Si la connexion est retourné à la piscine, il n'y a rien que vous assurer de recevoir la même connexion de la piscine, la deuxième fois.
Cependant, dans votre test concret, vous vérifiez la sous-jacentes IdbConnection, ce qui je suppose est une partie de ADO.NET et n'est-il pas ADO.NET le regroupement de connexion que vous souhaitez tester dans ce cas? Ce que vous devez faire est de créer deux sessions différentes (de la même usine ainsi, assurez-vous que c'est le cas) et assurez-vous que vous recevez le même connexion.
Au début de chaque opération de la classe du Pilote (dans mon cas OdbcDriver) crée un nouveau DbConnection (OdbcConnection). Cette connexion reste ouverte à l'ensemble de la transaction qui est inutile. Le test que j'ai écrit l'utilise en fait deux séances différentes à partir d'une SessionFactory.
Je ne suis toujours pas sûr que la vérification .L'état est juste un test pour s'. Regarde msdn.microsoft.com/en-us/library/... il est dit: "l'Appel de la propriété de l'Etat sur une connexion ouverte augmente la demande de surcharge, car chacun de ces appels provoque une SQL_ATTR_CONNECTION_DEAD appel à la sous-jacentes pilote ODBC pour déterminer si la connexion est toujours valide." Cela indique qu'une "Fermé" connexion peut être une connexion morte, à cet effet, n'est pas réutilisable à la piscine.
en outre: msdn.microsoft.com/en-us/library/..., dit le texte suivant: "Indique l'état de l'occurrence de SqlConnection lors de la plus récente de l'exploitation du réseau a été réalisée sur la connexion." Ce qui m'amène à penser que l'État n'a pas vraiment représentent ce que l'état actuel de la connexion est dans ce cas, et ne peut pas être utilisé pour s'assurer que les connexions sont disponibles à la piscine. Et la mise en commun peut aussi être un pilote, je sais pour un fait que Npgsql a construit dans le cadre de mise en commun, et ne pas compter sur ADO.NET pour cela.

OriginalL'auteur Antineutrino | 2011-09-27