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);
}
}
}
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
Vous devez vous connecter pour publier un commentaire.
NHibernate a deux "modes".
sessionfactory.OpenSession(connection)
.sessionfactory.OpenSession()
Il ya un certain soutien pour
TransactionScope
. C'est probablement à l'aide de la première de "mode". Probablement la connexion n'est pas tenue par NH, mais par l'étendue de la transaction. Je ne sais pas exactement, je n'en ai pas l'utilisation de l'environnement de transactions.NH est à l'aide de la ADO.NET connexion de la piscine par le moyen.
Vous pouvez également vous déconnecter de la session à l'aide de
ISession.Disconnect()
et reconnectez-vous en utilisantISession.Reconnect()
.Dans le la documentation vous trouverez:
OriginalL'auteur Stefan Steinegger
Vous pouvez accomplir cela en ajoutant les paramètres suivants à votre chaîne de connexion.
Mise en commun: permet la mise en commun de votre application
Min Piscine: Le nombre minimum de connexions pour les garder ouverts, même lorsque toutes les sessions sont fermées.
Max de la Piscine: Le nombre max de connexions de l'application s'ouvrira à la DB. Lorsque le maximum est atteint, il faudra attendre pour le nombre de secondes spécifié par le Délai d'attente de Connexion, puis lancer une exception.
Délai d'attente de connexion: durée Maximale (en secondes) d'attente pour une connexion gratuite et de la piscine
De connexion durée de Vie: Lorsqu'une connexion est retourné à la piscine, au moment de sa création est comparée à l'heure actuelle, et la connexion est détruit si ce laps de temps (en secondes) dépasse la valeur spécifiée par la Connexion à Vie. Une valeur de zéro (0) causes de connexions regroupées pour avoir le maximum de délai d'attente de connexion.
Incr Piscine Taille: Contrôle le nombre de connexions qui sont établies lorsque toutes les connexions sont utilisées.
Decr Piscine Taille: Contrôle le nombre de connexions qui sont fermés quand une quantité excessive de connexions établies sont inutilisés.
http://www.codeproject.com/Articles/17768/ADO-NET-Connection-Pooling-at-a-Glance
OriginalL'auteur zlsmith86