2011-08-06 17 views
11

tengo followed the pattern on this site para conectar ninject y nhibernate a mi sitio asp.net-mvc3.¿Cómo debe usar el patrón UnitofWork en mi sitio asp.net-mvc (usando nhibernate y ninject)

Este es el código en mis global.aspx.cs:

internal class ServiceModule : NinjectModule 
{ 
    public override void Load() 
    { 
     var helper = new NHibernateHelper(connectionString); 
     Bind<ISessionFactory>().ToConstant(helper.SessionFactory) 
      .InSingletonScope(); 

     Bind<IUnitOfWork>().To<UnitOfWork>() 
      .InRequestScope(); 
     Bind<ISession>().ToProvider(new SessionProvider()) 
      .InRequestScope(); 
     Bind<IIntKeyedRepository<FAQ>>().To<Repository<FAQ>>() 
      .InRequestScope(); 
     } 

el tema es que ahora la necesidad de hacer Update() y Add() en mis reguladores;

Tengo esto como mi código del controlador:

public FAQController(IIntKeyedRepository<FAQ> faqRepository, IUnitOfWork unitOfWork) 
    { 
     _faqRepository = faqRepository; 
     _unitOfWork = unitOfWork; 
    } 


    [Authorize] 
    [AcceptVerbs(HttpVerbs.Post)] 
    [ValidateInput(false)] 
    public ActionResult AddFAQ(FAQ contact) 
    { 
     var c = new FAQ {Question = contact.Question, Answer = contact.Answer}; 
     _faqRepository.Add(c); 
     _unitOfWork.Commit(); 
     return RedirectToAction("Index"); 
    } 

mi pregunta principal es que se siente mal para pasar en Iunitofwork en el constructor como muchas otras acciones no lo necesitan. Realmente solo lo necesito para las acciones donde realizo actualizaciones y las inserto en mi db. Como estoy usando ninject IOC en el enlace de arriba, parece decir pasar este objeto de unidad de trabajo a través de IOC.

Entonces, ¿hay una manera mejor y más optimizada para usar el patrón UnitOfWork con IOC en asp.net-mvc que llama a beingtransaction para cada método en mi controlador.

+0

Tome un vistazo a [esta] (http://blog.xelibrion.com/journal/2011/4/16/nhibernate-session-management-in-aspnet-mvc -application.html) publicación de blog. – xelibrion

Respuesta

12

Una forma alternativa de manejar transacciones es usar un IActionFilter Abrir la transacción en OnActionExecuting y comprometerse en OnActionExecuted

public class TransactionFilter : IActionFilter 
{ 
    private readonly ISession session; 
    private ITransaction transaction; 

    public TransactionFilter(ISession session) 
    { 
     this.session = session; 
    } 

    public void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     this.transaction = this.session.BeginTransaction(); 
    } 

    public void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     try 
     { 
      if (this.transaction.IsActive) 
      { 
       if (filterContext.Exception == null) 
       { 
        this.transaction.Commit(); 
       } 
       else 
       { 
        this.transaction.Rollback(); 
       } 
      } 
     } 
     finally 
     { 
      this.transaction.Dispose(); 
     } 
    } 
} 

Definir un atributo para marcar las acciones que utilizan una transacción:

[AttributeUsage(AttributeTargets.Method)] 
public class TransactionAttribute : Attribute 
{ 
} 

Cambie su configuración de Ninject:

internal class ServiceModule : NinjectModule 
{ 
    public override void Load() 
    { 
     var helper = new NHibernateHelper(connectionString); 
     Bind<ISessionFactory>().ToConstant(helper.SessionFactory) 
      .InSingletonScope(); 

     Bind<ISession>().ToProvider<SessionProvider>().InRequestScope(); 
     Bind(typeof(IRepository<>)).To(typeof(Repository<>)); 
     Bind(typeof(IIntKeyedRepository<>)).To(typeof(Repository<>)); 
     BindFilter<TransactionFilter>(FilterScope.Action, null) 
      .WhenActionMethodHas<TransactionAttribute>(); 
    } 
} 

Finalmente cambiar su controlador:

public FAQController(IIntKeyedRepository<FAQ> faqRepository) 
{ 
    _faqRepository = faqRepository; 
} 


[Transaction] 
[Authorize] 
[AcceptVerbs(HttpVerbs.Post)] 
[ValidateInput(false)] 
public ActionResult AddFAQ(FAQ contact) 
{ 
    var c = new FAQ {Question = contact.Question, Answer = contact.Answer}; 
    _faqRepository.Add(c); 
    return RedirectToAction("Index"); 
} 
+0

Eso es exactamente lo que estaba buscando. Ayende propuso casi la misma solución: http://ayende.com/blog/4809/refactoring-toward-frictionless-odorless-code-what-about-transactions Pero prefiero inyectar la sesión a la solicitud del controlador. – Tim

0

Por lo general, trato de mantener oculta la implementación genérica de IRepository dentro de IUnitOfWork (ver a continuación).

Mi otra recomendación es pasar un UnitOfWorkProvider o UnitOfWorkFactory al constructor. De esta forma, puede registrar el alcance de la transacción localmente. Esto tiene la ventaja adicional de poder resolver IRepository o ISession como mejor le parezca, a través de la inyección de dependencia o manualmente.

using(var uow = this.UnitOfWorkProvider.New()) 
{ 
    uow.Save<Faq>(myFaq); 
} 

también hacen Seguro en su IUnitOfWork.Dispose() a limpiar la transacción y cualquier objetos de sesión de datos/información que pueda tener.

0

Prefiero solo inyectar mi unidad de trabajo en las clases que realmente las usan. En la mayoría de los casos, las clases de persistencia (Repositorio en mi caso) son las únicas que necesitan la unidad de trabajo. Desea asegurarse de mantener una separación clara de las preocupaciones. El controlador no necesita conocer la unidad de trabajo y tampoco debe estar acoplado a ella.

public class FaqRepository { 
    public FaqRepository(IUnitOfWork unitofWork) { ... } 

    public void CreateQuestion(Faq faq) { 
    unitOfWork.Save(faq); 
    unitOfWork.Commit(); 
    } 
} 

Si está invocando su repositorio desde su controlador, inyecte el repositorio en el controlador de la siguiente manera:

public class FaqController { 
    public FaqController(IFaqRepository faqRepository) {...} 
} 

¿Eso tiene sentido?

+0

El problema que veo con esto es que su Repositorio ahora es responsable de la UoW. Si necesita realizar cambios que requieren dos repositorios, se encontrará con problemas. – Phill

+0

@Phil - ¿puedes dar más detalles? – csano

+0

Si su UoW cae sobre dos objetos, por ejemplo, UserLogin y UserProfile se almacenan por separado, sin cascada, está en dos repositorios y desea ejecutar ambos dentro de la transacción. Su repositorio actualmente compromete la transacción, por lo que debe ejecutar dos Unidades de trabajo separadas. – Phill

Cuestiones relacionadas