存储库模式方法的标准化

时间:2010-04-21 15:29:55

标签: c# design-patterns repository-pattern

我试图找出存储库模式的正确定义。

我最初的理解是这个(非常愚蠢)

  • 将Business Objects与数据对象分开
  • 标准化数据访问层中的访问方法。

我确实看到过两种不同的实现方式,并且没有正式的在线示例,我看到的那些都隐藏在书中。

实施1:

public Interface IRepository<T>{
      List<T> GetAll();
      void Create(T p);
      void Update(T p);
}


public interface IProductRepository: IRepository<Product> {
      //Extension methods if needed
       List<Product> GetProductsByCustomerID();
}

实施2:

public interface IProductRepository {
      List<Product> GetAllProducts();
      void CreateProduct(Product p);
      void UpdateProduct(Product p);
      List<Product> GetProductsByCustomerID();
}

注意第一个是通用Get / Update / GetAll等,第二个更像是我要定义的“DAO”之类的。

两者共享数据实体的提取。我喜欢哪个,但我可以用简单的DAO做同样的事情。然而,第二部分标准化我看到的访问操作的价值,如果你实现这个企业范围,人们很容易知道你的存储库的访问方法集。

我错误地认为数据访问的标准化是这种模式的一个组成部分吗?如果两者都正确,为什么选择执行2?

Rhino有一篇关于实施1的好文章,当然MS有一个模糊的definition,而实施2的例子是here

7 个答案:

答案 0 :(得分:16)

我是第二个由奥德引用的福勒引用。我想指出他说“集合 - 喜欢”界面。你如何实现像界面这样的集合当然取决于你,但你既不能也不应该试图隐藏它代表远程数据源的事实。因此,它与内存中集合显着不同,内存集合不需要刷新对远程数据存储的更改。 ORM的更改跟踪机制或您自己的滚动解决方案决定了对调用者的透明程度。删除通常需要明确标记,插入是可发现的(可达性持久性),有时也需要明确标记更新。将它与你的聚合根的复杂依赖关系结合起来,你会发现它不是那样的集合。

没有“cannonical repository implementation”这样的东西。

通用存储库基类的拥护者和喜欢自己实现每个存储库的人之间经常发生争执。虽然通用实现在简单的场景中很有吸引力,但您经常会发现它是一个非常漏洞的抽象。例如,您的某些聚合可能只是软删除(通过虚方法覆盖可分解),而其他聚合可能根本不支持删除操作。

在决定采用哪条路线之前,请确保了解每种方法的含义。 Greg Young在通用存储库的优点上发表了很好的帖子。

http://codebetter.com/blogs/gregyoung/archive/2009/01/16/ddd-the-generic-repository.aspx

答案 1 :(得分:7)

来自Martin Fowler“企业应用程序架构的模式”,存储库模式的定义是:

  

使用类似集合的接口访问域对象,在域和数据映射层之间进行调解。

所以,两种方法都是正确的。

答案 2 :(得分:4)

我非常喜欢通用存储库模式,但我认为你应该强烈考虑不要直接从接口继承,因为它可能会成为一个非常大的限制,特别是因为通用接口的代码很多次都会与它相同可以在抽象基类中定义,您将不再能够在类中拥有多个通用存储库。

我建议让你的IProductRepository实现者通过委托访问泛型IRepository<Product>并通过构造函数注入它,这样你就可以编写你的可能很多IRepositories的类,并以一种有意义的方式将它们分组到一个接口后面。

我写了一篇关于这个主题的博客,虽然它专门引用了NHibernate这个模式可以应用于任何类型的存储库:Creating a common generic and extensible NHiberate Repository version 2

答案 3 :(得分:2)

随着.NET中LINQ的引入,通用存储库模式变得更容易实现:

public interface IRepository<T> : IQueryable<T>
{
    void Add(T item);
    void Remove(T item);
}

要获得资格作为存储库,它只需要能够访问底层存储中的数据(由IQueryable轻松提供)并修改包含的数据。

您可以为基本接口提供扩展,以提供更多特定于实体的行为的挂钩(例如连接到基于SQL的存储库的存储过程调用),但大多数操作可以通过简单的接口完成。

答案 4 :(得分:1)

除了通用存储库接口(实现1)和特定于角色的存储库(实现2)的变体之外,您还可以考虑使用通用方法存储库:

public interface IRepository
{
    void Save<ENTITY>(ENTITY entity) where ENTITY : DomainEntity;

    ENTITY Load<ENTITY>(Guid id) where ENTITY : DomainEntity;

    IQueryable<ENTITY> Query<ENTITY>() where ENTITY : DomainEntity;

    IQueryable<ENTITY> Query<ENTITY>(IDomainQuery<ENTITY> whereQuery)
        where ENTITY : DomainEntity;
}

第三个版本来自Jimmy Bogard的this blogpost,他也表达了对通用存储库接口的偏好。 我通常使用通用存储库baseclass来实现这个接口;这样,我只需要为每个域实体实现不同的东西。

答案 5 :(得分:0)

我通常使用具有组合而不是继承的通用存储库。这为我提供了通用实现的优势,可以控制要公开的方法。

这样的事情:

public Interface IRepository<T>{
  List<T> GetAll();
  void Create(T p);
  void Update(T p);
}


public interface IProductRepository {
  //Extension methods if needed
   List<Product> GetProductsByCustomerID();
   List<T> GetAll();
   void Create(T p);
   //Let assume here you should not be able to update the products
}

public ProductRepository : IProductRepository {
    private IRepository _repository;

    public ProductRepository(IRepository repository) {
        this._repository = repository;
    }

       List<T> GetAll() 
       {
            _repository.GetAll();
       }

       void Create(T p) 
       {
            _repository.Create(p);
       }

       List<Product> GetProductsByCustomerID() 
       {
          //..implementation goes here
       }
}

答案 6 :(得分:0)

存储库模式是软件开发中最常用的模式之一。可以将许多帖子标记为您问题的答案。 我想强调的一点是,如果你使用IoC(Autofac,Windsor等),将会改善良好的存储库实现。我很久以前一直在玩一些基于ADO.NET的框架(LinqToSql,EF)和NHibernate。如果使用IoC,您始终可以从通用实现中受益。 您可以为特定存储库定义接口,并在确实需要某些特定操作时解决。