2009-10-26 14 views
6

Qual è l'approccio migliore per la gestione della transazione NHibernate utilizzando Autofac all'interno dell'applicazione Web?NHibernate con Autofac all'interno di ASP.NET (MVC): ITransaction

Il mio approccio alla sessione è

builder.Register(c => c.Resolve<ISessionFactory>().OpenSession()) 
     .ContainerScoped(); 

Per ITransaction, ho found an example su Google Code, ma si basa su HttpContext.Current.Error momento di decidere se rollback.

C'è una soluzione migliore? E quale scopo ha la transazione NHibernate ?

risposta

4

ho postato qualche tempo fa:

http://groups.google.com/group/autofac/browse_thread/thread/f10badba5fe0d546/e64f2e757df94e61?lnk=gst&q=transaction#e64f2e757df94e61

modificato, in modo che l'intercettore ha la registrazione capacità e l'attributo [transazione] può essere utilizzato anche in una classe.

[global::System.AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] 
public class TransactionAttribute : Attribute 
{ 
} 


public class ServicesInterceptor : Castle.Core.Interceptor.IInterceptor 
{ 
    private readonly ISession db; 
    private ITransaction transaction = null; 

    public ServicesInterceptor(ISession db) 
    { 
     this.db = db; 
    } 

    public void Intercept(IInvocation invocation) 
    { 
     ILog log = LogManager.GetLogger(string.Format("{0}.{1}", invocation.Method.DeclaringType.FullName, invocation.Method.Name)); 

     bool isTransactional = IsTransactional(invocation.Method); 
     bool iAmTheFirst = false; 

     if (transaction == null && isTransactional) 
     { 
      transaction = db.BeginTransaction(); 
      iAmTheFirst = true; 
     } 

     try 
     { 
      invocation.Proceed(); 

      if (iAmTheFirst) 
      { 
       iAmTheFirst = false; 

       transaction.Commit(); 
       transaction = null; 
      } 
     } 
     catch (Exception ex) 
     { 
      if (iAmTheFirst) 
      { 
       iAmTheFirst = false; 

       transaction.Rollback(); 
       db.Clear(); 
       transaction = null; 
      } 

      log.Error(ex); 
      throw ex; 
     } 
    } 

    private bool IsTransactional(MethodInfo mi) 
    { 
     var atrClass = mi.DeclaringType.GetCustomAttributes(false); 

     foreach (var a in atrClass) 
      if (a is TransactionAttribute) 
       return true; 

     var atrMethod = mi.GetCustomAttributes(false); 

     foreach (var a in atrMethod) 
      if (a is TransactionAttribute) 
       return true; 

     return false; 
    } 
} 
+1

Mi piace la soluzione. Tuttavia, un'altra domanda è su quali metodi dovrei applicare [Transazione]? Secondo questo http://nhprof.com/Learn/Alert?name=DoNotUseImplicitTransactions sembra che dovrei sempre avere una transazione se ho una sessione, tuttavia questa soluzione mi richiede di aggiungere manualmente [Transaction] ovunque sia usata la sessione. –

+0

Controllare la versione modificata. Probabilmente saprai come modificarlo da lì. È anche possibile saltare l'attributo [Transazione] e avviare sempre la transazione. È la vostra scelta. Avevo bisogno dell'attributo, perché a volte avevo bisogno di fare 2 cose in diverse transazioni. ho fatto in questo modo: // attributo Nessuna transazione CallThis pubblici virtuali vuoto() { TranS1(); // commit Trans2(); // impegna } [transazione] pubblico virtuale vuoto Trans1() {} [transazione] pubblico virtuale vuoto trans2() {} – dmonlord

+0

Ci può spiegare il caso d'uso per due operazioni? Questo è importante perché se potessi incontrare un caso d'uso simile, vedrò il problema in modo diverso. –

-1

solito gestire la transazione me ..

public ActionResult Edit(Question q){ 
try { 
using (var t = repo.BeginTransaction()){ 
    repo.Save(q); 
    t.Commit(); 
    return View(); 
} 
catch (Exception e){ 
    ... 
} 
} 
+1

È la soluzione più semplice, ma è un po 'noioso e aggiunge il rientro all'intero metodo. –

+0

sicuro, 2 righe e un rientro, ma un giorno ti rendi conto che devi fare due transazioni nella stessa richiesta o qualcosa del genere .. –

+1

Alla maggior parte delle persone non piace premere ctrl + c/ctrl + v così tante volte – Paco

4

Quando uso autofac io uso lo stesso metodo contenitore di ambito, ma invece di passare la stessa sessione per il mio repository/oggetti DAO mi passano un UnitOfWork che è il contenitore ambito. L'Unità di lavoro ha questo nel costruttore.

private readonly ISession _session; 
    private ITransaction _transaction; 

    public UnitOfWork(ISession session) 
    { 
     _session = session; 
     _transaction = session.BeginTransaction(); 
    } 

E il Dispose è:

public void Dispose() 
    { 
     try 
     { 
      if (_transaction != null && 
          !_transaction.WasCommitted && 
          !_transaction.WasRolledBack) 
       _transaction.Commit(); 
      _transaction = null; 
     } 
     catch (Exception) 
     { 
      Rollback(); 
      throw; 
     } 

    } 

Io sono (ab) utilizzando la roba di smaltimento deterministica in autofac al fine di gestire questo, e ben mi po 'come esso.

L'altra cosa è che fondamentalmente sto solo prendendo di mira un ambiente ASPNet e ho preso una decisione consapevole che una transazione è legata a una richiesta web. Quindi una transazione per modello di richiesta web.

A causa di ciò posso fare questo codice di gestione in un IHttpModule errore:

void context_Error(object sender, System.EventArgs e) 
    { 
     _containerProvider.RequestContainer.Resolve<IUnitOfWork>().Rollback(); 
    } 

non ho dato un'occhiata a NHibernate.Burrow troppo da vicino, ma sono sicuro che ci sia qualcosa là che fa la maggior parte di questo.

+0

La tua idea è molto simile a quella di Autofac, e mi piace molto. Sfortunatamente, non posso accettare due risposte, quindi sceglierò quella che è stata la prima (anche il dmonlord ha molta meno reputazione). –

Problemi correlati