2010-09-22 6 views
5

L'extrait de code suivant fonctionne très bien avec SQL Server 2008 (SP1) mais avec Oracle 11g l'appel à session.BeginTransaction() lève une exception avec le message 'La connexion fait déjà partie d'un local ou d'un transaction distribuée »(trace de la pile ci-dessous). En utilisant le "" NHibernate.Driver.OracleDataClientDriver ".NHibernate TransactionScope problème avec Oracle 11g

Est-ce que quelqu'un d'autre a rencontré cela?

using (var scope = new TransactionScope()) 
{ 
    using (var session = sessionFactory.OpenSession()) 
    using (var transaction = session.BeginTransaction()) 
    { 
     // do what you need to do with the session 
     transaction.Commit(); 
    } 
    scope.Complete(); 
} 
 
    Exception at: at NHibernate.Transaction.AdoTransaction.Begin(IsolationLevel isolationLevel) 
      at NHibernate.Transaction.AdoTransaction.Begin() 
      at NHibernate.AdoNet.ConnectionManager.BeginTransaction() 
      at NHibernate.Impl.SessionImpl.BeginTransaction() 
      at MetraTech.BusinessEntity.DataAccess.Persistence.StandardRepository.SaveInstances(List`1& dataObjects) in S:\MetraTech\BusinessEntity\DataAccess\Persistence\StandardRepository.cs:line 3103 

     Inner error message was: Connection is already part of a local or a distributed transaction 
     Inner exception at: at Oracle.DataAccess.Client.OracleConnection.BeginTransaction(IsolationLevel isolationLevel) 
      at Oracle.DataAccess.Client.OracleConnection.BeginDbTransaction(IsolationLevel isolationLevel) 
      at System.Data.Common.DbConnection.System.Data.IDbConnection.BeginTransaction() 
      at NHibernate.Transaction.AdoTransaction.Begin(IsolationLevel isolationLevel) 

Répondre

0

Une question, pourquoi fais-tu la session.BeginTransaction intérieure - depuis 2.1 GA NHibernate s'inscrira automatiquement dans des contextes TransactionScope donc il n'y a aucune raison de faire votre propre plus.

+3

Il pourrait être d'obtenir le autoflush NHibernate fonctionner correctement – Konstantin

7

Le problème en utilisant uniquement la portée de la transaction est décrite ici: NHibernate FlushMode Auto Not Flushing Before Find

Il semble NHibernate (v3.1 avec le dialecte oracle et 11g db w/opd.net v2.112.1.2) exige que ses propres transactions pour éviter le problème de vidage, mais je n'ai pas pu obtenir la portée de la transaction pour travailler avec les transactions nhibernate.

Je ne peux pas sembler le faire fonctionner :( cela pourrait être un défaut dans NHibernate ou odp.net, pas sûr ...

trouvé ici même problème: NHibernate 3.0: TransactionScope and Auto-Flushing

FIXES : trouvé une solution! en mettant "enlist = dynamic;" dans ma chaîne de connexion oracle, le problème a été résolu.J'ai été capable d'utiliser à la fois la transaction nhibernate (pour résoudre le problème de flush) et la portée de la transaction:

 ISessionFactory sessionFactory = CreateSessionFactory(); 

     using (TransactionScope ts = new TransactionScope()) 
     { 
      using (ISession session = sessionFactory.OpenSession()) 
      using (ITransaction tx = session.BeginTransaction()) 
      { 
       //do stuff here 

       tx.Commit(); 

      } 
      ts.Complete(); 
     } 

J'ai vérifié mes fichiers journaux et trouvé ceci: 2011-06-27 14: 03: 59852 [10] DEBUG NHibernate.Impl.AbstractSessionImpl - enrôlé dans la transaction DTC: Serializable

avant que SQL a été exécuté sur la connexion. Je vais effectuer un test unitaire pour confirmer la bonne exécution. Je ne suis pas trop sûr de ce que serializable me dit si

2

Brads réponse, en utilisant un TransactionScope externe et une transaction interne NHibernate avec enlist = dynamique, ne semble pas fonctionner correctement. Ok, les données sont validées.

Mais si vous omettez le scope.Complete() ou déclenchez une exception après tx.Commit(), les données sont toujours validées (pour Oracle)! Cependant, pour une raison quelconque, cela fonctionne pour SQL Server. Les transactions NHibernate prennent en charge le vidage automatique, mais à la fin, elles appellent la transaction ADO.NET sous-jacente. Alors que de nombreuses sources encouragent le modèle ci-dessus comme meilleure pratique pour NHibernate pour résoudre le auto-flush issue, les sources discutant ADO.NET natif disent le contraire: N'utilisez PAS TransactionScope et les transactions internes ensemble, pas pour Oracle et pas pour SQL-Server. (Voir this question et my answer)

Ma conclusion: Ne pas associer les transactions TransactionScope et NHibernate. Pour utiliser TransactionScope, ignorez les transactions NHibernate et gérez manuellement le vidage (voir aussi NHibernate Flush doc).

0

De Cookbook NHibernate

Rappelez-vous que NHibernate nécessite une transaction NHibernate lors de l'interaction avec la base de données. TransactionScope n'est pas un substitut. Comme illustré dans l'image suivante, TransactionScope doit entourer complètement la transaction de session et NHibernate. L'appel à TransactionScope.Complete() doit avoir lieu après la suppression de la session. Tout autre ordre entraînera vraisemblablement de vilaines erreurs de production, telles que des fuites de connexion. Je pense aussi que cela devrait fonctionner avec TransactionScope, mais pas dans 3.3.x.x ni dans la version 4.0.0.400.

La recette ci-dessus peuvent travailler, mais ont besoin de le tester avec TrancactionScope imbriquée, avec TransactionScope intérieure qui a un Transaction.Suppress défini (lors de l'utilisation de SQL), etc ...