2010-08-29 180 views
0

我正在關注從a previous question。我接受的答案涉及使用通用的IRepository來處理基本的CRUD,其中包含域指定的IMovieRepository,它委託給通用設置。進一步的細節包括其在通用IRepository一個WrapQueryInSession方法:單元測試應該知道NHibernate嗎?

IEnumerable<T> WrapQueryInSession(Func<ISession, IEnumerable<T>> query); 

我得到落實時,我意識到,這暴露了NHibernate的ISession消費者的一般存儲庫。 NHibernate完全包含在IRepository實現中,但對於該方法簽名。

本就浮出水面時,我想單元測試MovieRepository,由具有IRepository,在RepositoryFake實施,傳遞給MovieRepository構造:

protected override void BeforeEachTest() 
{ 
    _fixture = new MovieRepository(new RepositoryFake()); 
} 

我的測試類有一個私有假倉庫實現:

private class RepositoryFake : IRepository<Movie> 
{ 
    ... 
    public IEnumerable<Movie> WrapQueryInSession(Func<ISession, IEnumerable<Movie>> query) 
    { 
     ... 
    } 
    ... 
} 

這樣設置的方式,測試類和IRepository實現的任何其他使用者知道來自NHibernate的,以及NHibernate本身。這似乎是一個漏洞抽象的例子。

有沒有更好的方法來完全包含在一個IRepository實現中使用NHibernate?

回答

2

my answer to your previous question的想法是,通用的IRepository只在您的基礎架構層內部是已知的 - 它不會在此之外發布。當您將ISession發佈到非通用存儲庫時,他們獲得了一個非常通用的界面,因爲他們可以訪問ISession進行查詢。與未暴露的Isession的問題是,你的通用倉庫要麼:

  1. 限制你的查詢功能或
  2. 有不同的方法進行查詢(基本上覆制的ISession接口一大堆

似乎浪費一點已經NHibernate的查詢界面隱藏掉一個門面(其中通用庫將限於)內。

IMO,如果你選擇NHibernate的,你應該充分利用的動力我t給你並且依賴你的基礎設施DLL(包括測試)。將通用IRepository接口想象爲NHibernate的輔助接口,以減少存儲庫中的重複代碼量。

+0

我沒有想到這樣的說法,但現在它開始合理。這就像一個謎題:碎片正在放置,圖片現在正在形成:) – 2010-08-30 05:37:21

+0

從另一方面來看,一旦我有了這個釘子,我應該可以創建一個模板用於其他項目。 – 2010-08-30 05:46:15

2

我同意NHibernate不應該被完全抽象的原則,但我發現NHibernate的查詢接口可以隱藏起來,沒有太多的麻煩,並且可以通過使用Query對象來實現。每個查詢對象都應該利用NHibernate提供的強大功能(ISession,ICriteria,IQuery等),並且可以通過實施IRepository來執行。

讓Query對象代替存儲庫中的方法提供了更好的可測試性,並且它不需要在測試類中引用NHibernate。

這裏是整個事情會怎麼看起來像:

public interface IRepository 
{ 
     ISession Session { get; } 
     TResult Query<TResult>(IQuery<TResult> query); 
} 

public class Repository : IRepository 
{ 
    public ISession Session 
    { 
     get { return /* Call the session factory to return an ISession */; } 
    } 

    public TResult Query<TResult>(IQuery<TResult> query) 
    { 
     return query.Execute(Session)); 
    } 
} 

public interface IQuery<TResult> 
{ 
    TResult Execute(QueryContext context); 
} 

public abstract class Query<TResult> : IQuery<TResult> 
{ 
    public abstract TResult Execute(ISession session); 
} 

public class GetPeopleByName: IQuery<Person> 
{ 
    private readonly string _name; 

    public GetPeopleByName(string name) 
    { 
     _name = name; 
    } 

    public override IList<Person> Execute(ISeesion session) 
    { 
     var query = context.Session.CreateCriteria(typeof(Person)) 
      .Add(Restrictions.Eq("Name", _name)); 

     return query.List<Person>(); 
    } 
} 

然後你就可以用上面的一樣:

IRepository repository = /* Get somehow the implementation */ 
IList<Person> people = repository.Execute(new GetPeopleByName("Anna")); 
相關問題