2009-10-26 16 views
6

Jakie jest najlepsze podejście do zarządzania transakcją NHibernate za pomocą Autofac w aplikacji internetowej?NHibernate z Autofac w ASP.NET (MVC): ITransaction

Moje podejście do sesji jest

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

Dla ITransaction mam found an example na Google Code, ale opiera się na HttpContext.Current.Error przy podejmowaniu decyzji o wycofaniu.

Czy istnieje lepsze rozwiązanie? A jaki zakres ma NHibernate transakcja powinna mieć?

Odpowiedz

4

Zamieściłem to jakiś czas temu:

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

modyfikacji, tak aby przechwytujących ma możliwość rejestrowania i [] transakcja atrybut może również być używane na zajęciach.

[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

Podoba mi się rozwiązanie. Jednak inne pytanie dotyczy jakich metod powinienem zastosować [Transakcja]? Zgodnie z tym http://nhprof.com/Learn/Alert?name=DoNotUseImplicitTransactions wydaje się, że zawsze powinienem mieć transakcję, jeśli mam sesję, jednak to rozwiązanie wymaga ręcznego dodawania [Transakcji] wszędzie tam, gdzie używana jest sesja. –

+0

Sprawdź zmodyfikowaną wersję. Prawdopodobnie będziesz wiedział, jak go zmodyfikować. Możesz także całkowicie pominąć atrybut [Transakcja] i zawsze rozpoczynać transakcję. To Twój wybór. Potrzebowałem tego atrybutu, ponieważ czasami musiałem robić 2 rzeczy w różnych transakcjach. zrobiłem to tak: // atrybut No transakcja public virtual void CallThis() { Trans1(); // zatwierdza Trans2(); // zobowiązuje } [Transakcja] public virtual void Trans1() {} [Transakcja] public virtual void TRANS2 umieszczany() {} – dmonlord

+0

Mógłbyś wyjaśnić przypadek użycia dla dwóch transakcji? Jest to ważne, ponieważ jeśli mogę napotkać podobny przypadek użycia, inaczej przyjrzę się problemowi. –

-1

zwykle zarządzać transakcją siebie ..

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

To najprostsze rozwiązanie, ale jest nieco żmudne i dodaje wcięcie do całej metody. –

+0

jasne, 2 linie i jedno wcięcie, ale pewnego dnia zdajesz sobie sprawę, że musisz zrobić dwie transakcje w tym samym żądaniu lub coś podobnego .. –

+1

Większość ludzi nie lubi nacisnąć ctrl + c/ctrl + v tyle razy – Paco

4

Gdy używam autofac mogę używać tej samej metody pojemnik scoped ale zamiast przechodzenia tej samej sesji do mojego repozytorium obiektów DAO/I zdać UnitOfWork że pojemnik jest zawężona. Jednostka pracy ma to w konstruktorze.

private readonly ISession _session; 
    private ITransaction _transaction; 

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

I Usuwać jest:

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

    } 

Jestem (ab) przy użyciu deterministycznego wyrzucaniu rzeczy w autofac, aby poradzić sobie z tym, a także ja coś jak to.

Inną sprawą jest to, że zasadniczo kierujemy się wyłącznie do środowiska ASPNet i świadomie podjąłem decyzję, że transakcja jest powiązana z żądaniem internetowym. Tak więc transakcja na wzór żądania strony internetowej.

powodu, że mogę zrobić tego błędu kod obsługi w IHttpModule:

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

nie podjęły obejrzenia NHibernate.Burrow zbyt dokładnie, ale jestem pewien, że jest tam coś, że ma większość tego.

+0

Twój pomysł jest bardzo podobny do tego, co zrobił Autofac i bardzo mi się podoba. Niestety, nie mogę zaakceptować dwóch odpowiedzi, więc wybiorę ten, który był pierwszy (również dronlord ma znacznie mniejszą reputację). –

Powiązane problemy