2011-03-17 42 views
2

做研究設計IRepository<T>結構的最佳方式,我遇到了一個名爲「白板」(http://whiteboardchat.codeplex.com/)的項目,同時通過一些論壇爲NHProfnHibernate [TransactionAttribute]爲UoW衝突與存儲庫模式

我挖掘了一段時間的源代碼,發現MVC的一個非常有趣的屬性叫做TransactionAttribute,定義如下: (我已經做了簡短的調整以適應我的IoC解決方案)

using System; 
using System.Linq; 

using Ninject; 

namespace System.Web.Mvc 
{ 
    /// <summary> 
    /// This will allow ASP.NET MVC to apply Transactions to the controllers. 
    /// </summary> 
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] 
    public class TransactionAttribute : ActionFilterAttribute 
    { 
     [Inject] 
     public NHibernate.ISession Session 
     { 
      get; 
      set; 
     } 

     public override void OnActionExecuting(ActionExecutingContext filterContext) 
     { 
      Session.BeginTransaction(); 
     } 

     public override void OnActionExecuted(ActionExecutedContext filterContext) 
     { 
      if (Session.Transaction.IsActive) 
      { 
       if (filterContext.Exception == null) 
       { 
        Session.Flush(); 
        Session.Transaction.Commit(); 
       } 
       else 
       { 
        Session.Transaction.Rollback(); 
       } 
      } 
     } 
    } 
} 

這真的很有趣;有用的,但是有關它的事情困擾我。當我使用NHProf運行我的查詢時,它給了我關於'沒有正確使用交易'的警告,並建議我將所有查詢包含在Transaction中。好吧,這是優秀和良好...

於是我去裝飾我Repository<T> : IRepository<T>類這樣的...

public T Update(T instance) 
    { 
     using (var transaction = session.BeginTransaction()) 
     { 
      // attempt to perform the given update 
      session.SaveOrUpdate(instance); 

      try 
      { 
       // commit the transaction to the database 
       transaction.Commit(); 

       // update succeeded, so we'll return true 
       return instance; 
      } 
      catch 
      { 
       // restore the database to its previous state if we failed. 
       transaction.Rollback(); 

       // update failed, so return a null object 
       return default(T); 
      } 
     } 
    } 

這裏是我遇到的問題。

無論我讀什麼,常見做法是總是使用存儲庫添加到集合。然而TransactionAttribute,這本身就被帶到了我的注意由Ayende Rahien的博客,誰是我所知的NHProf的主要開發者之一,並在此白板項目工作的人之一,使得假設您在MVC控制器級執行存儲庫命令。

那它是哪一個呢?我現在完全困惑於我的交易邏輯應該採取最佳做法。我從字面上找到了相互矛盾的答案,並且在某些情況下來自同一個人。

回答

1

你不應該處理倉庫內的交易。一個控制器(如你)或HTTP模塊應該啓動並提交/回滾事務。保存或更新不應該孤立地完成。他們將在控制器的操作結束時承諾。這樣您就可以利用ADO批處理和其他NHibernate功能。

此外,請確保將nhibernate ISession的FlushMode設置爲Commit。

+0

這對我來說很有意義,但它並不能幫助NHPROF仍然給我提供有關事務管理的警告。你能看到我爲什麼仍然被認爲是'違反'的使用我發佈的屬性的任何理由嗎? – Ciel 2011-03-19 13:27:22

+0

交易屬性看起來不錯。如果您將存儲庫更新(實例)方法更改爲「session.SaveOrUpdate(instance);」,您是否仍然在收到事務警告? – 2011-03-19 15:41:11

0

您是否正在用[Transaction]屬性裝飾您的操作方法或控制器類?如果沒有,這個動作過濾器代碼甚至不會被調用。

此外,您還需要確保將會話對象[Inject]保存到您的存儲庫中,並且會話對象的範圍限定爲請求。

爲例:

public class MyRepository 
{ 
    [Inject] 
    public ISession Session { get; set; } 

    public void Save(MyModel model) { Session.Save(model); } 
} 

public class MyController : Controller 
{ 
    [Inject] 
    public MyRepository MyRepository { get; set; } 

    [Transaction] 
    public ActionResult Save(MyModel model) 
    { 
     MyRepository.Save(model); 
    } 
} 

和註冊會話時;

var configuration = new NHibernateConfiguration(); 
Bind<ISessionFactory>().ToConstant(configuration.GetSessionFactory()); 
Bind<ISession>().ToMethod(x => x.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope(); 

通知InRequestScope()部分

+0

是的,我欣賞帖子,但我已經解決了這個問題。我會發布我在下面解決的問題。 – Ciel 2011-03-30 12:52:44

0

發佈本作@Fatal。

最初的回答回答了我的問題,但這不可避免地是我最終爲避免使用方法級屬性而做的。

我沒有聲明代碼來控制屬性中的事務,而是將它包含在我爲Ninject的ISession管理中。

 Bind<ISession>() 
      .ToMethod(c => OpenSession()) 
      .InRequestScope() 
      .OnActivation(session => 
      { 
       session.BeginTransaction(); 
       session.FlushMode = FlushMode.Commit; 
      }) 
      .OnDeactivation(session => 
      { 
       if (session.Transaction.IsActive) 
       { 
        try 
        { 
         session.Transaction.Commit(); 
        } 
        catch 
        { 
         session.Transaction.Rollback(); 
        } 
       } 
      }); 

這樣做是開放的,其中一個ISession注入新ISession每個請求,當它被激活時,開始新的事務。此時,Ninject現在跟蹤狀態並處理回滾的後果,從而實現一個非常簡單的工作模式單元。

我不知道這是否是世界上最好的方法,但我已經向少數人展示過它,但它並沒有因爲不好的練習而被擊落,而且迄今爲止它的效果很好。