实施Repository模式的最佳方法是什么?
我一直在探索BDD / DDD,并试图提出一个适当的Repository模式的实现。 到目前为止,很难就实现这一目标的最佳方式达成共识。 我试图把它归结为以下变化,但我不确定哪个是最好的方法。
作为参考,我正在用NHibernate构build一个ASP.MVC应用程序作为后端。
public interface IRepository<T> { // 1) Thin facade over LINQ T GetById(int id); void Add(T entity); void Update(T entity); void Remove(T entity); IQueryable<T> Find(); // or possibly even T Get(Expression<Func<T, bool>> query); List<T> Find(Expression<Func<T, bool>> query); } public interface IRepository<T> { // 2) Custom methods for each query T GetById(int id); void Add(T entity); void Update(T entity); void Remove(T entity); IList<T> FindAll(); IList<T> FindBySku(string sku); IList<T> FindByName(string name); IList<T> FindByPrice(decimal price); // ... and so on } public interface IRepository<T> { // 3) Wrap NHibernate Criteria in Spec pattern void Add(T entity); void Update(T entity); void Remove(T entity); IList<T> FindAll(); IList<T> FindBySpec(ISpecification<T> specification); T GetById(int id); } public interface IRepository<T> { // 4) Expose NHibernate Criteria directly T GetById(int id); void Add(T entity); void Update(T entity); void Remove(T entity); IList<T> FindAll(); IList<T> Find(ICriteria criteria); // .. or possibly IList<T> Find(HQL stuff); }
我最初的想法是
1)从效率的angular度来看是很棒的,但随着事情变得更加复杂,我可能会陷入困境。
2)看起来非常单调乏味,最终会出现一个非常拥挤的类,但在我的域逻辑和我喜欢的数据层之间提供了高度的分离。
3)在前面看起来很困难,而且还有更多的工作要写查询,但限制了交叉污染仅仅是规范层。
4)我最不喜欢的,但可能是最直接的实现,也可能是最复杂的查询数据库效率,虽然它在调用代码上承担了很大的责任。
我认为他们都是很好的select(如果你不想把自己绑定到nhibernate,也许只有4个),你似乎有很好的分析和利弊分析,根据你目前的努力做出自己的决定。 不要太狠狠地打这个
我目前正在研究2和3之间的混合物我想:
public interface IRepository<T> { ... IList<T> FindAll(); IList<T> FindBySpec(ISpecification<T> specification); T GetById(int id); } public interface ISpecificRepository : IRepository<Specific> { ... IList<Specific> FindBySku(string sku); IList<Specific> FindByName(string name); IList<Specific> FindByPrice(decimal price); }
还有一个Repository(T)基类。
“以上都不是”的方法也有一个很好的论点。
通用存储库的问题在于,你假定系统中的所有对象都将支持所有四个CRUD操作:创build,读取,更新,删除。 但是在复杂的系统中,您可能只有一些对象只支持一些操作。 例如,您可能有只读的对象,或创build但从未更新的对象。
你可以把IRepository接口分成小的接口,读取,删除等等,但是很快就会变得麻烦。
Gregory Young提出了一个很好的论点(从DDD /软件分层的angular度),每个存储库应该只支持特定于域对象或你正在使用的聚合的操作。 这是他关于通用存储库的文章。
而另一种观点,请参阅Ayende 博客文章 。
我们所做的一件事就是我们所有的仓库都有不同的需求,所以我们要创build一个接口集合:
public interface IReadOnlyRepository<T,V> { V Find(T); }
在这个例子中,只读存储库只是从数据库中获取。 T,V的原因是V代表仓库返回的内容,T代表传入的内容,所以你可以这样做:
public class CustomerRepository:IReadOnlyRepository<int, Customer>, IReadOnlyRepository<string, Customer> { public Customer Find(int customerId) { } public Customer Find(string customerName) { } }
我也可以为添加,更新和删除创build单独的界面。 这样,如果我的存储库不需要的行为,那么它只是不实现接口。
当使用NH和Linq时,你的仓库可以是:
session.Linq<Entity>()
规格是处理的事情:
IQueryable<Entity>
如果你愿意的话,你可以把它全部拿走,但这是抽象抽象的很多世俗的工作。
简单很好。 嗯,新罕布什尔州数据库,但它提供了更多的模式。 除了DAL之外的其他因素还远不止于罪孽。
我是1的bif风扇,因为我可以创buildfilter和分页扩展方法,而不是将其应用于Find方法的IQueryable <>返回值。 我将扩展方法保留在数据层中,然后在业务层中即时构build。 (不完全纯粹,诚然。)
当然,当系统稳定时,我可以select使用相同的扩展方法来创build特定的Find方法,并使用Func <>进行优化。
枯萎仓库
LosTechies的Jimmy Bogard的相关文章
http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/10/wither-the-repository.aspx
此外,另一篇快速文章,其中一些评论build议版本#2是一个DOA模式,而不是一个存储库。
http://fabiomaulo.blogspot.com/2009/06/linq-and-repository.html
我相信这取决于你的需求。 将存储库与您考虑使用的其他devise模式结合起来考虑是非常重要的。 最后,这取决于你对仓库的期望是什么(使用它的主要原因是什么)。
你是否需要创build严格的层(例如将来需要用entity frameworkreplaceNHibernate)? 你想特别写入testing库方法?
没有最好的方式来创build存储库。 只有几种方法,这绝对取决于你,什么是最适合你的需求。