方法的存储库模式标准化
Posted
技术标签:
【中文标题】方法的存储库模式标准化【英文标题】:Repository Pattern Standardization of methods 【发布时间】:2011-02-10 16:12:03 【问题描述】:我只是想找出存储库模式的正确定义。
我最初的理解是这样的(非常愚蠢)
将业务对象与数据对象分开 标准化数据访问层的访问方法。我确实见过 2 种不同的实现,网上也没有正式的例子,我看到的那些都藏在书里了。
实施 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。
【问题讨论】:
对我而言,接口是一种抽象,即与实现相反。我们在这里讨论的只是接口,还是实现类? 【参考方案1】:来自 Martin Fowler “企业应用程序架构的模式”,存储库模式的定义是:
使用类集合接口访问域对象,在域和数据映射层之间进行调解。
所以,这两种方法都是正确的。
【讨论】:
我想整个“类似收藏”是含糊的,我认为收藏就像获取/更新/删除/等不是..但我假设你会认为获取相同= 获取产品【参考方案2】:我是通用存储库模式的忠实拥护者,但我认为您应该强烈考虑不要直接从接口继承,因为它可能会成为一个非常大的限制,特别是因为很多时候通用接口的代码将与它相同可以在一个抽象基类中定义,您将不再能够在一个类中拥有超过 1 个通用存储库。
我建议让您的 IProductRepository 实现者通过委托访问通用 IRepository<Product>
并通过构造函数将其注入,这样您就可以组合可能有许多 IRepositories 的类,并以有意义的方式将它们分组到单个接口后面。
我写了一篇关于这个主题的博客,其中特别提到了 NHibernate,这种模式可以应用于任何类型的存储库:Creating a common generic and extensible NHiberate Repository version 2
【讨论】:
【参考方案3】:随着 .NET 中 LINQ 的引入,通用存储库模式变得更容易实现:
public interface IRepository<T> : IQueryable<T>
void Add(T item);
void Remove(T item);
要成为存储库,它只需要能够访问底层存储中的数据(IQueryable
很容易提供)并修改包含的数据。
您可以为基本接口提供扩展,为更多特定于实体的行为(例如连接到基于 SQL 的存储库的存储过程调用)提供挂钩,但大多数操作都可以通过简单接口完成。
【讨论】:
【参考方案4】:我支持 oded 引用的 Fowler 名言。我想指出他说的是“收藏-like”界面。您如何实现类似集合的接口当然取决于您,但您既不能也不应该试图隐藏它代表远程数据源的事实。因此,它与内存中的集合有很大不同,后者不需要将更改刷新到远程数据存储。您的 ORM 或您自己的解决方案的更改跟踪机制决定了这对调用者的透明程度。删除通常需要显式标记,插入是可发现的(通过可达性持久化),更新有时也需要显式标记。将此与聚合根的复杂依赖关系结合起来,您会发现这不是很像集合。
没有“规范的存储库实现”之类的东西。
通用存储库基类的拥护者和那些更喜欢单独实现每个存储库的人之间一直在进行一场斗争。虽然通用实现在简单场景中很有吸引力,但您经常会发现它是一个非常容易泄漏的抽象。例如,您的某些聚合可能仅被软删除(可通过虚拟方法覆盖进行调整),而其他聚合可能根本不支持删除操作。
在决定采用哪种方法之前,请确保您了解每种方法的含义。 Greg Young 有一篇关于通用存储库优点的好文章。
http://codebetter.com/blogs/gregyoung/archive/2009/01/16/ddd-the-generic-repository.aspx
【讨论】:
存储库的全部意义在于抽象出与持久性相关的概念。其中一些通常放置在工作单元中,仅需要在您的基础架构中使用,以便在您的应用程序代码处理请求后自动刷新更改。如果您的存储库看起来像 DAO,那么您只是在重新执行 DAO,这不是重点。【参考方案5】:除了您的通用存储库接口(实施 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,他还表达了对通用存储库接口的偏好。 我通常使用实现此接口的通用存储库基类来遵循它;这样,我只需要为每个域实体实现不同的东西。
【讨论】:
【参考方案6】:我通常使用具有组合而不是继承的通用存储库。这给了我通用实现的优势,可以控制公开哪些方法。
类似这样的:
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
【讨论】:
【参考方案7】:存储库模式是软件开发中最常用的模式之一。许多帖子可以标记为您的问题的答案。 我想强调的一点是,如果您使用 IoC(Autofac、Windsor 等),好的存储库实现将会得到改进。我很久以前就玩过一些基于 ADO.NET 的框架(LinqToSql、EF)和 NHibernate。如果您使用 IoC,您总是可以从通用实现中受益。 您可以为您的特定存储库定义接口,并在您确实需要一些特定操作时解决。
【讨论】:
以上是关于方法的存储库模式标准化的主要内容,如果未能解决你的问题,请参考以下文章