存储库模式 方法标准化
我只是想找出存储库模式的正确定义。
我最初的理解是(极其愚蠢)
- 将业务对象与数据对象分开,
- 标准化数据访问层中的访问方法。
我确实见过两种不同的实现,网上没有正式的例子,我见过的都藏在书本里。
实现 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?
All I am trying to find out the correct definition of the repository pattern.
My original understanding was this (extremely dumbed down)
- Separate your Business Objects from your Data Objects
- Standardize access methods in data access layer.
I have really seen 2 different implementation, and there are no formal examples online, the ones i have seen are tucked away in books.
Implementation 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();
}
Implementation 2 :
public interface IProductRepository {
List<Product> GetAllProducts();
void CreateProduct(Product p);
void UpdateProduct(Product p);
List<Product> GetProductsByCustomerID();
}
Notice the first is generic Get/Update/GetAll, etc, the second is more of what I would define "DAO" like.
Both share an extraction from your data entities. Which I like, but i can do the same with a simple DAO. However the second piece standardize access operations I see value in, if you implement this enterprise wide people would easily know the set of access methods for your repository.
Am I wrong to assume that the standardization of access to data is an integral piece of this pattern ? If both are correct why would one choose to do implementation 2?
Rhino has a good article on implementation 1, and of course MS has a vague definition and an example of implementation 2 is here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我赞同奥德引用的福勒的话。我想指出的是,他说的是“类似于集合”的界面。如何实现类似集合的接口当然取决于您,但是您不能也不应该尝试隐藏它代表远程数据源的事实。因此,它与内存中集合有很大不同,后者不需要将更改刷新到远程数据存储。 ORM 或您自己的解决方案的更改跟踪机制决定了这对调用者的透明程度。删除通常需要显式标记,插入是可发现的(通过可达性持久化),更新有时也需要显式标记。将其与聚合根的复杂依赖关系相结合,您会发现这不太像集合。
不存在“规范存储库实现”之类的东西。
通用存储库基类的拥护者和那些喜欢单独实现每个存储库的人之间一直存在着一场斗争。虽然通用实现在简单场景中很有吸引力,但您经常会发现它是一个非常有漏洞的抽象。例如,您的某些聚合可能只能软删除(可通过虚拟方法覆盖进行删除),而其他聚合可能根本不支持删除操作。
在决定采取哪条路线之前,请确保您了解每种方法的含义。 Greg Young 有一篇关于通用存储库优点的好文章。
https://web.archive.org/web/20090204223739/http://codebetter.com/blogs/gregyoung/archive/2009/01/16/ddd-the-generic-repository.aspx< /a>
I second the Fowler quote cited by oded. I want to point out that he said "collection-like" interface. How you implement the collection like interface is certainly up to you, but neither can nor should you try to hide the fact it represents a remote datasource. It therefore differs significantly from an in-memory collection, which does not need to flush changes to a remote data store. The change tracking mechanism of your ORM or your roll-your-own solution determines how transparent this can be made to the caller. Deletes usually need to be marked explicitly, inserts are discoverable (persistence by reachability) and updates sometimes need to be marked explicitly too. Combine this with the complicated dependencies of your aggregate roots and you'll see that's not very collection like.
There is no such thing as "the cannonical repository implementation".
There is a constant battle going on between the advocators of a generic repository base class and those who prefer implementing each repository on its own. While the generic implementation is appealing in simple scenarios, you will very often find it to be a very leaky abstraction. For example some of your aggregates may only be soft-deleted (cistomizable via virtual method overrides) while others may not support a delete operation at all.
Make sure you understand the implications of each approach before deciding which route to take. Greg Young has a good post on the merits of generic repositories.
https://web.archive.org/web/20090204223739/http://codebetter.com/blogs/gregyoung/archive/2009/01/16/ddd-the-generic-repository.aspx
从Martin Fowler的《企业应用架构模式》中,存储库模式的定义是:
所以,这两种做法都是正确的。
From Martin Fowler "Patterns of Enterprise Application Architecture", the definition of the Repository Pattern is:
So, both approaches are correct.
我是通用存储库模式的忠实粉丝,但我认为您应该强烈考虑不直接从接口继承,因为它可能成为一个非常大的限制,特别是因为很多时候通用接口的代码将与它可以定义的代码相同在抽象基类中,您将无法再在类中拥有超过 1 个通用存储库。
我建议您的 IProductRepository 实现者通过委托访问通用的
IRepository
并通过构造函数将其注入,这样您就可以组合可能包含多个 IRepositories 的类,并将它们分组在一个接口后面,方式如下:有道理。我写了一篇关于这个主题的博客,其中特别引用了 NHibernate,这种模式可以应用于任何类型的存储库: 创建通用通用且可扩展的 NHiberate 存储库版本 2
I am a great fan of the generic repository pattern but I think you should strongly consider not directly inheriting from the interface as it can become a very large limitation especially since many times the code for the generic interface will be the same that it could be defined in an abstract base class that you will no longer be able to have more than 1 generic repository inside a class.
I recommend having your IProductRepository implementer access the generic
IRepository<Product>
through delegation and inject that in through the constructor so you can compose your class of possibly many IRepositories and group them behind a single interface in a way that makes sense.I wrote a blog on this topic while it specifically references NHibernate this pattern can be applied to any type of repository: Creating a common generic and extensible NHiberate Repository version 2
随着 .NET 中 LINQ 的引入,通用存储库模式变得更容易实现:
要成为存储库,它只需要能够访问底层存储中的数据(由
IQueryable
轻松提供) )并修改包含的数据。您可以提供基本接口的扩展,以提供更多特定于实体的行为的挂钩(例如连接到基于 SQL 的存储库的存储过程调用),但大多数操作都可以通过简单的接口完成。
With the introduction of LINQ in .NET, a generic repository pattern becomes much easier to realize:
To qualify as a repository, it merely needs to be able to access data in the underlying store (easily provided by
IQueryable
) and modify the contained data.You can provide extensions to the base interface to provide hooks for more entity-specific behaviour (such as wiring into a stored procedure call for a SQL-based repository), but the majority of operations can be completed by the simple interface.
除了通用存储库接口(实现 1)和特定于角色的存储库(实现 2)的变体之外,您还可以考虑通用方法存储库:
第三个版本来自 Jimmy Bogard 的这篇博文,他在文中还表达了对泛型的偏好存储库接口。
我通常使用实现此接口的通用存储库基类来遵循它;这样,我只需要实现每个域实体不同的东西。
In addition to your generic repository interface (implementation 1) and your variation on the role-specific repository (implementation 2) you can also consider a generic method repository:
This third version comes from this blogpost by Jimmy Bogard, where he also expresses preference for the generic repository interface.
I usually follow that with a generic repository baseclass which implements this interface; that way, I only have to implement the stuff that is different for each domain entity.
我通常使用具有组合而不是继承的通用存储库。这给了我通用实现的优势,并且可以控制要公开的方法。
像这样的东西:
I usually use the generic repository with composition instead of inheritance. That gives me the advantage of a generic implementation, with the control of which methods to expose.
Something like this:
存储库模式是软件开发中最常用的模式之一。有很多帖子可以标记为您问题的答案。
我想强调的是,如果使用 IoC(Autofac、Windsor 等),良好的存储库实现将会得到改进。我很久以前就一直在玩一些基于 ADO.NET 的框架(LinqToSql、EF)和 NHibernate。如果使用 IoC,您总是可以从通用实现中受益。
您可以为特定存储库定义接口,并在确实需要某些特定操作时进行解析。
The repository pattern is one of the most used pattern in software development. The are many post that can be marked as answer to your question.
Something that i like to highlight is the fact that a good repository implementation will be improved if you use IoC (Autofac, Windsor, etc...). I have been playing long time ago with some ADO.NET based frameworks (LinqToSql, EF) and NHibernate. You always can have benefits from a generic implementation if you use IoC.
You can define interfaces for your specific repositories and resolve when you really need some specific actions.