38

我一直在探索 BDD/DDD,因此试图提出存储库模式的正确实现。到目前为止,很难就实现这一点的最佳方式达成共识。我试图将其归结为以下变体,但我不确定哪种方法是最好的。

作为参考,我正在构建一个以 NHibernate 作为后端的 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)从效率的角度来看很好,但是随着事情变得更加复杂,我可能会遇到麻烦。

2) 看起来很乏味,最终可能会出现一个非常拥挤的课程,但否则会在我喜欢的领域逻辑和数据层之间提供高度分离。

3) 似乎很难预先编写查询并且需要更多的工作来编写查询,但是将交叉污染限制在 Specs 层。

4) 我最不喜欢,但可能是最直接的实现,并且对于复杂的查询可能是最高效的数据库,尽管它给调用代码带来了很多责任。

4

7 回答 7

22

对于“以上都不是”方法也有很好的论据。

通用存储库的问题在于,您假设系统中的所有对象都支持所有四种 CRUD 操作:创建、读取、更新、删除。但在复杂系统中,您可能会拥有仅支持少数操作的对象。例如,您可能有只读对象,或者创建但从未更新的对象。

您可以将 IRepository 接口分解为小的接口,用于读取、删除等,但这很快就会变得混乱。

Gregory Young 提出了一个很好的论点(从 DDD / 软件分层的角度来看),每个存储库应该只支持特定于您正在使用的域对象或聚合的操作。这是他关于通用存储库的文章。

如需其他视图,请参阅此 Ayende博客文章

于 2009-09-12T00:14:39.723 回答
10

我认为它们都是不错的选择(如果您不想将自己束缚在休眠状态,则可能是 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 (of T) 基类。

于 2009-09-11T02:43:39.913 回答
6

我们正在做的一件事是我们所有的存储库都有不同的需求,因此我们正在创建一个接口集合:

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)
    {
    }
}

我还可以为添加、更新和删除创建单独的界面。这样,如果我的存储库不需要该行为,那么它就不会实现接口。

于 2009-09-11T03:58:33.960 回答
1

我是 1 的 bif 粉丝,因为我可以创建过滤器和分页扩展方法,而不是应用于 Find 方法的 IQueryable<> 返回值。我将扩展方法保留在数据层中,然后在业务层中动态构建。(诚​​然,并非完全纯净。)

当然,当系统稳定时,我可以选择使用相同的扩展方法制作特定的 Find 方法并使用 Func<> 进行优化。

于 2009-09-11T03:11:46.587 回答
1

将 NH 与 Linq 一起使用时,您的存储库可以是:

session.Linq<Entity>()

规范是处理的事情:

IQueryable<Entity>

如果你愿意,你可以把它全部隐藏起来,但抽象一个抽象是很多平凡的工作。

简单就好。是的,NH 有数据库,但它提供了更多的模式。除了 DAL 之外,还依赖于 NH 远非罪过。

于 2009-09-11T14:55:10.010 回答
1

我相信这取决于您的需求。将存储库与您考虑使用的其他设计模式结合起来考虑是很重要的。最后,这很大程度上取决于您对存储库的期望(使用它的主要原因是什么)。

您是否需要创建严格的层(例如,您将来需要将 NHibernate 替换为 Entity Framework)?您想特别为存储库方法编写测试吗?

没有最好的方法来创建存储库。只有几种方法,这绝对取决于您,哪种方法最适合您的需求。

于 2015-09-07T18:17:43.130 回答
0

枯萎存储库

LosTechies 的 Jimmy Bogard 的相关文章

http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/10/wither-the-repository.aspx

此外,另一篇带有一些评论的快速文章建议版本 #2 实际上是 DOA 模式,而不是存储库。

http://fabiomaulo.blogspot.com/2009/06/linq-and-repository.html

于 2009-09-11T16:21:28.633 回答