2009-10-26 18 views
6

¿Cuál es el mejor enfoque para gestionar la transacción NHibernate utilizando Autofac en la aplicación web?NHibernate con Autofac dentro de ASP.NET (MVC): ITransaction

Mi acercamiento a la sesión es

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

Para ITransaction, tengo found an example en Google Code, pero depende de HttpContext.Current.Error la hora de decidir si se debe reversión.

¿Existe una solución mejor? ¿Y qué alcance NHibernate transacción debe tener?

Respuesta

4

Me ha publicado esto hace un tiempo:

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

modificado, de manera que el interceptor tiene la capacidad y la tala [Transacción] atributo puede también se usará en una clase.

[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

Me gusta la solución. Sin embargo, otra pregunta es ¿qué métodos debo aplicar [Transacción]? De acuerdo con este http://nhprof.com/Learn/Alert?name=DoNotUseImplicitTransactions, parece que siempre debería tener una transacción si tengo una sesión, sin embargo, esta solución requiere que agregue manualmente [Transaction] en todos los lugares donde se usa la sesión. –

+0

Compruebe la versión modificada. Probablemente sabrá cómo modificarlo desde allí. También puede omitir el atributo [Transaction] por completo y siempre iniciar la transacción. Es tu elección. Necesitaba el atributo, porque a veces tenía que hacer 2 cosas en diferentes transacciones. Lo hice así: // Sin atributo de transacción vacío virtual público CallThis() { Trans1(); // confirma Trans2(); // compromete } [Transacción] pública virtual vacío Trans1() {} [Transacción] pública virtual vacío Trans2() {} – dmonlord

+0

Podría explicar el caso de uso de dos operaciones? Esto es importante ya que si también encuentro un caso de uso similar, veré el problema de forma diferente. –

-1

Por lo general las arreglo yo la transacción ..

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

Es la solución más simple, pero es un poco tediosa y agrega sangría a todo el método. –

+0

seguro, 2 líneas y una sangría, pero algún día te das cuenta de que tienes que hacer dos transacciones en la misma solicitud o algo así ... –

+1

La mayoría de las personas no les gusta presionar ctrl + c/ctrl + v tantas veces – Paco

4

Cuando uso autofac uso el mismo método de contenedores de ámbito pero en lugar de pasar la misma sesión a mi repositorio/objetos DAO que pase un UnitOfWork que es de ámbito contenedor. La Unidad de trabajo tiene esto en el constructor.

private readonly ISession _session; 
    private ITransaction _transaction; 

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

Y dispose es:

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

    } 

soy (ab) uso de la materia disposición determinista en autofac el fin de gestionar esto, y así en cierto modo me gusta mucho.

La otra cosa es que básicamente me estoy enfocando en un entorno ASPNet y tomé la decisión consciente de que una transacción está vinculada a una solicitud web. Entonces, una transacción por patrón de solicitud web.

Debido a que yo puedo hacer esto en un código de manejo de errores IHttpModule:

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

no he echado un vistazo a NHibernate.Burrow demasiado de cerca, pero estoy seguro de que hay algo allí que hace la mayor parte de esta.

+0

Tu idea es muy similar a lo que Autofac hizo, y me gusta mucho. Lamentablemente, no puedo aceptar dos respuestas, así que elegiré la que fue primero (también dmonlord tiene mucha menos reputación). –

Cuestiones relacionadas