2011-08-06 19 views
11

Mam followed the pattern on this site do podpięcia ninject i nhibernate do mojej strony asp.net-mvc3.Jak powinieneś używać wzorca UnitofWork na mojej stronie asp.net-mvc (przy użyciu nhibernate i ninject)

Oto kod w moich 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(); 
     } 

problem jest to, że teraz trzeba zrobić update() i Add() w moim kontrolerów;

mam to jako mój kod kontrolera:

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"); 
    } 

moim głównym pytaniem jest to, że czuje się źle przechodzą w Iunitofwork w konstruktorze jak wiele innych działań nie trzeba go. Naprawdę potrzebuję tego tylko dla akcji, w których robię aktualizacje i wstawiam do mojej bazy danych. Ponieważ używam ninja IOC na powyższym łączu, wydaje się, że przekazuję ten obiekt unitofwork przez IOC.

Czy istnieje lepszy, bardziej zoptymalizowany sposób korzystania z wzorca UnitOfWork z IOC w asp.net-mvc, który wywołuje betransakcję dla każdej metody w moim kontrolerze.

+0

przyjrzeć [to] (http://blog.xelibrion.com/journal/2011/4/16/nhibernate-session-management-in-aspnet-mvc -application.html) wpis na blogu. – xelibrion

Odpowiedz

12

Alternatywnym sposobem obsługi transakcji jest użycie IActionFilter Otwórz transakcję w OnActionExecuting i popełnić na 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(); 
     } 
    } 
} 

Definiowanie atrybutu do oznaczania działań, które używają transakcji:

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

Zmień konfigurację programu 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>(); 
    } 
} 

końcu zmienić Kontroler:

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

Tego właśnie szukałem. Ayende zaproponowała prawie to samo rozwiązanie: http://ayende.com/blog/4809/refactoring-toward-frictionless-odorless-code-what-about-transactions Ale wolę wstrzykiwanie sesji przez żądanie od kontrolera. – Tim

0

Generalnie staram się utrzymać moją ogólną implementację IRepository ukrytą w IUnitOfWork (patrz poniżej).

Moje inne zalecenie to przekazanie UnitOfWorkProvider lub UnitOfWorkFactory do konstruktora. W ten sposób możesz zarejestrować zasięg transakcji lokalnie. Ma to tę dodatkową zaletę, że jest w stanie rozwiązać IRepository lub ISIS zgodnie z Twoim wyobrażeniem, poprzez wtrysk zależności lub ręcznie.

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

Również upewnić się Państwo w swoim IUnitOfWork.Dispose() oczyścić transakcji oraz wszelkich obiektów sesji danych/informacji można mieć.

0

Wolę tylko wstrzykiwać moją jednostkę pracy w klasy, które faktycznie z nich korzystają. W większości przypadków klasy uporania (repozytorium w moim przypadku) są jedynymi, które potrzebują jednostki pracy. Chcesz mieć pewność, że zachowujesz czysty rozdział obaw. Kontroler nie musi wiedzieć o jednostce pracy i nie powinien być z nią powiązany.

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

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

Jeśli powołując repozytorium od kontrolera, wstrzyknąć repozytorium do kontrolera w następujący sposób:

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

Czy to ma sens?

+0

Problemem, który widzę z tym, jest to, że Twoje Repozytorium jest teraz odpowiedzialne za UU. Jeśli chcesz wprowadzić zmiany wymagające dwóch repozytoriów, napotkasz problemy. – Phill

+0

@Phil - czy możesz rozwinąć? – csano

+0

Jeśli twoja wersja UoW zawiera dwa obiekty, powiedzmy, UserLogin i UserProfile są przechowywane osobno, bez kaskadowania, jest między dwoma repozytoriami i chcesz uruchomić oba w ramach transakcji. Twoje repozytorium obecnie zatwierdza transakcję, więc musisz uruchomić dwie oddzielne jednostki pracy. – Phill