2010-08-29 85 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")); 
相关问题