代码之家  ›  专栏  ›  技术社区  ›  Michael Cook

实现存储库模式的最佳方法?

  •  35
  • Michael Cook  · 技术社区  · 17 年前

    我一直在研究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)前面看起来很困难,编写查询的工作也更多,但交叉污染仅限于规范层。

    4)我最不喜欢,但可能是最直接的实现,也可能是对复杂查询最有效的数据库,尽管它对调用代码负有很大的责任。

    7 回复  |  直到 11 年前
        1
  •  10
  •   Fredy Treboux    17 年前

    我认为它们都是很好的选择(如果你不想把自己和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);
    }
    

    还有一个(t)基类的存储库。

        2
  •  22
  •   dthrasher    17 年前

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

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

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

    GregoryYoung提出了一个很好的论点(从DDD/软件分层的角度),即每个存储库应该只支持特定于您所使用的域对象或聚合的操作。这是他的文章 generic repositories .

    另一种观点,请看这一页。 blog post .

        3
  •  6
  •   Michael Mann    17 年前

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

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

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

        4
  •  1
  •   Scott McKenzie    17 年前

    我是1的BIF爱好者,因为我可以创建过滤器和分页扩展方法,而不是应用于iQuery<>find方法的返回值。我将扩展方法保存在数据层中,然后在业务层中动态构造。(不完全是纯粹的,诚然。)

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

        5
  •  1
  •   anonymous    17 年前

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

    session.Linq<Entity>()
    

    规范是处理以下问题的内容:

    IQueryable<Entity>
    

    如果你愿意的话,你可以把它表面化,但这是一个抽象抽象的日常工作。

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

        6
  •  1
  •   Miroslav Holec    11 年前

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

    是否需要创建严格的层(例如,将来需要用实体框架替换nhibernate)?是否要特别将测试写入存储库方法?

    没有最好的方法来创建存储库。只有几种方法,这完全取决于你,什么是最适合你的需要。

        7
  •  0
  •   Michael Cook    17 年前

    摧毁仓库

    吉米·博加德的一篇相关文章

    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