我正在追踪a previous question。我接受的答案涉及使用通用IRepository
来处理基本CRUD,其中包含委托给通用设置的特定域IMovieRepository
。进一步的细节涉及在通用WrapQueryInSession
上使用IRepository
方法:
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的ISession
,因此NHibernate本身。这似乎是抽象漏洞的情况。
是否有更好的方法可以在IRepository
实现中完全包含NHibernate的使用?
答案 0 :(得分:2)
来自my answer to your previous question的想法是,通用IRepository仅在您的基础架构层中已知 - 它不会在此之外发布。当您将ISession发布到非通用存储库时,它们获得了一个非常通用的接口,因为它们可以访问ISession进行查询。不暴露ISession的问题是您的通用存储库将:
看起来有点浪费,NHibernate的查询界面隐藏在一个立面内(通用存储库将被限制)。
IMO,如果您选择nHibernate,您应该利用它为您提供的功能,并在整个基础架构dll(包括测试)中依赖。将通用IRepository接口视为NHibernate的辅助接口,以减少存储库中重复代码的数量。
答案 1 :(得分: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"));