2014-10-30 30 views
2

讓我從我當前的設置開始,然後解釋我想實現的目標。我們使用NHibernate並試圖用Ninject實現IRepository/IUnitOfWork模式。理想情況下,它應該適用於任何使用代碼的應用程序,無論是ASP.Net,WCF還是其他。ninject注入iunitofwork到存儲庫作用域屬性

IUnitOfWork

public interface IUnitOfWork 
{ 
    object Add(object obj);//all other supported CRUD operations we want to expose 
    void Commit(); 
    void Rollback(); 
} 

的UnitOfWork

public class UnitOfWork : IUnitOfWork 
{ 
    private readonly ISessionFactory _sessionFactory; 
    private readonly ISession _session; 
    private readonly ITransaction _transaction; 

    public UnitOfWork(ISessionFactory sessionFactory) 
    { 
     _sessionFactory = sessionFactory; 
     _session = _sessionFactory.OpenSession(); 
     _transaction = _session.BeginTransaction(); 
    } 

    public object Add(object obj) 
    { 
     return _session.Save(obj); 
    } 

    public void Commit() 
    { 
     if(!_transaction.IsActive) 
     {throw new Exception("some error");} 
     _transaction.Commit(); 
    } 

    public void Rollback() 
    { 
     if (!_transaction.IsActive) 
     { 
      throw new Exception("some other error"); 
     } 
     _transaction.Rollback(); 
    } 
} 

IRepository

public interface IRepository<TEntity, TId> where TEntity : class 
{ 
    TId Add(TEntity item);//add other missing CRUD operations 
} 

GenericRepository

public class GenericRepository<TEntity, TId> : IRepository<TEntity, TId> 
    where TEntity : class 
{ 
    public TId Add(TEntity item) 
    { 
     throw new NotImplementedException(); 
    } 
} 

我使用Ninject作爲我的IOC容器。目標是在創建UnitOfWork的生命週期中重複使用相同的IUnitOfWork。無論調用的應用程序是什麼,我都希望實現的生命週期能夠正常工作,否則我會像使用大多數在線提示一樣使用InRequestScope。 我能夠做這樣的事:

//constructor 
public MyService(IUnitOfWork uow, IRepository<User, int> userRepo, IRepository<Cat, int> catRepo) 
{ 
    _uow = uow; _userRepo = userRepo; _catRepo = catRepo; 
} 

//method in same class 
public void DoSomeWork() 
{ 
    _userRepo.Add(someUser); 
    _catRepo.Add(someCat); 
    _uow.Commit(); 

    //rollback on error 
} 

我的綁定設置,如:

Bind<IUnitOfWork>.To<UnitOfWork>().InCallScope(); 
Bind(typeof(IRepository<,>)).To(typeof(GenericRepository<,>)); 

而這種綁定配置實際上適用於上述MyService,它會在一次創建的UnitOfWork構造函數,它將使用與IRepo相同的UnitOfWork函數,無論它實際上可能有多少層。

但是我想要做的就是將IUnitOfWork完全隱藏起來。我寧願提供一些可放置在方法之上的TransactionAttribute,它將在條目上創建IUnitOfWork,並將同一實例注入到TransactionAttribute範圍內的所有將來的IUnitOfWork請求中。它會相應地提交和回滾。所以上面的代碼會變成這樣:

//constructor 
public MyService(IRepository<User, int> userRepo, IRepository<Cat, int> catRepo) 
{ 
    _uow = uow; _userRepo = userRepo; _catRepo = catRepo; 
} 

//method in same class 
[Transaction] 
public void DoSomeWork() 
{ 
    _userRepo.Add(someUser); 
    _catRepo.Add(someCat); 
} 

是否有任何形式的結合設置我能做到這將使我來標記與[交易]的方法也是這樣嗎?我願意對IUnitOfWork和IRepository進行一些小的重構,而服務層代碼只是廢料代碼,所以我可以在那裏非常靈活。

回答

0

首先,注射不會

[Transaction] 
public void DoSomeWork() 
{ 
     _userRepo.Add(someUser); 
     _catRepo.Add(someCat); 
} 
畢竟

工作,容器不知道你會什麼樣的方法來調用,當你要調用它。

更進一步,因爲您希望它與WebApps,WCF一起工作..可能是一些石英工作或不是:您必須決定是否要將使用它的對象的使用期限(如MyService)與您想要的服務生存期相關聯(如IUnitOfWork/Repository/NHibernate Session)較長(例如單),並創建一個新的NHibernate Session對於像一個方法調用:

[Transaction] 
public void DoSomeWork() 
{ 
    _userRepo.Add(someUser); 
    _catRepo.Add(someCat); 
} 

這事實上可能被AOP使用FodyPostSharp實現,無論是。或者,您也可以使用裝飾器模式來實現此目的(請參閱here)。但是,如果我沒有記錯,ninject目前缺乏一些支持easz裝飾器處理的細節。

或者到[Transaction]屬性,你可以用控制一切明確啓動:

public interface IUnitOfWork 
{ 
    IUnitOfWorkSession Begin(); // starts a session and transaction 
} 

public interface IUnitOfWorkSession : IDisposable 
{ 
    void Commit(); 
    void Dispose(); // performs rollback in case no commit was performed 
} 

既然你將需要訪問NHibernate的Session中的其他對象,像_userRepo_catRepo您還需要一種方法來訪問因爲它與你如何/何時實例化對象無關。 當然,你可以沿着它傳遞這樣的:

public void DoSomeWork() 
{ 
    using(IUnitOfWorkSession session = _unitOfWork.Begin()) 
    {  
     _userRepo.Add(session, someUser); 
     _catRepo.Add(session, someCat); 
     session.Commit(); 
    } 
} 

但是,這不是真的很酷。因此,您需要使用類似於ThreadLocal(如果您使用異步/等待,這可能會有問題)或其他SynchronizationContext本地存儲。

現在,如果你已經設法達到這個目標,你可以考慮做AOP。 AOP將有兩個處理兩件事:

  • 獲得訪問IUnitOfWork。
    • 可以通過編織它作爲額外的構造函數的參數來完成
    • 或者它可以檢查是否已經有一個,如果有,它可以使用這一個,如果不是,就既可以拋出或編織論點...
  • 包裹裝飾的方法,像上述相同代碼:
public void DoSomeWork() 
{ 
    using(IUnitOfWorkSession session = _unitOfWork.Begin()) // weave this 
    {  
     _userRepo.Add(session, someUser); 
     _catRepo.Add(session, someCat); 
     session.Commit(); // weave this 
    } 
} 
+0

我們目前使用'PostSharp'(V2.1可悲)在其他組件,所以我知道我會用它來實現AOP。我的想法是,我的方面的OnEntry會開始一個新的'UnitOfWork'(我會忽略嘗試附加到現有的),但我如何確保創建的'UnitOfWork'是注入每個' IRepository'?我是否需要通過將方面的'UnitOfWork'粘貼到某種上下文機制(ThreadLocal/SynchronizationContext,就像您所提到的)? – cjablonski76 2014-10-31 13:13:14

+0

既然(至少這就是我干涉的內容),你不能也不想將「Session」的生命週期與使用它的對象的生命週期聯繫起來,你確實需要使用像ThreadLocal這樣的上下文機制或SynchronizationContext。 – BatteryBackupUnit 2014-10-31 13:44:33

+0

如果你不想使用這樣的上下文機制,每次執行DoSomeWork時都需要創建一個新的用戶回購,cat回購等。你可能最終會重新創建你的對象圖中相當大的一部分(取決於設計...)。有了上下文機制,你只能創建'Session',但你仍然需要這樣做(通過'IUnitOfWork.Begin()'或創建一個新的'IUnitOfWork'實例)。 – BatteryBackupUnit 2014-10-31 13:47:12