简体   繁体   English

每个实体的通用存储库或特定存储库?

[英]Generic Repository or Specific Repository for each entity?

Background背景

At the company I work for I have been ordered to update an old MVC app and implement a repository pattern for a SQL database.在我工作的公司,我被要求更新旧的 MVC 应用程序并为 SQL 数据库实现存储库模式。 I have created the context of the database using Entity Framework Database-First and got 23 entities.我已经使用 Entity Framework Database-First 创建了数据库的上下文并获得了 23 个实体。

The first question第一个问题

Do I need to create a repository for each entity or implement a generic repository for the context?我是否需要为每个实体创建一个存储库或为上下文实现一个通用存储库? I'm asking this because I have found following while searching internet:我问这个是因为我在搜索互联网时发现了以下内容:

One repository per domain每个域一个存储库

You should think of a repository as a collection of domain objects in memory.您应该将存储库视为内存中域对象的集合。 If you're building an application called Vega, you shouldn't have a repository like the following:如果您正在构建一个名为 Vega 的应用程序,您不应该拥有如下所示的存储库:

public class VegaRepository {}

Instead, you should have a separate repository per domain class, like OrderRepository, ShippingRepository and ProductRepository.相反,您应该为每个域类拥有一个单独的存储库,例如 OrderRepository、ShippingRepository 和 ProductRepository。

Source: Programming with Mosh: 4 Common Mistakes with the Repository Pattern资料来源: 使用 Mosh 编程:存储库模式的 4 个常见错误

The second question第二个问题

Does a generic repository work for Entity Framework Database-First?通用存储库是否适用于实体框架数据库优先? This is because I have found following while searching internet:这是因为我在搜索互联网时发现了以下内容:

Entity framework实体框架

Do note that the repository pattern is only useful if you have POCOs which are mapped using code first.请注意,存储库模式仅在您有首先使用代码映射的 POCO 时才有用。 Otherwise you'll just break the abstraction with the entities instead (= the repository pattern isn't very useful then).否则,您只会用实体打破抽象(= 那时存储库模式不是很有用)。 You can follow this article if you want to get a foundation generated for you.如果你想为你生成一个基金会,你可以关注这篇文章

Source: CodeProject: Repository pattern, done right来源: CodeProject:存储库模式,做得对

To begin with, if you are using full ORM like Entity Framework or NHibernate, you should avoid implementing additional layer of Repository and Unit Of Work.首先,如果您使用完整的 ORM,如实体框架或 NHibernate,您应该避免实现额外的存储库和工作单元层。 This is because;这是因为; the ORM itself exposes both Generic Repository and Unit Of Work. ORM 本身公开了通用存储库和工作单元。
In case of EF, your DbContext is Unit Of Work and DbSet is Generic Repository.在 EF 的情况下,您的DbContext是工作单元,而DbSet是通用存储库。 In case of NHibernate, it is ISession itself.在 NHibernate 的情况下,它是ISession本身。
Building new wrapper of Generic Repository over same existing one is repeat work.在相同的现有存储库上构建通用存储库的新包装器是重复工作。 Why reinvent the wheel?为什么要重新发明轮子?

But , some argue that using ORM directly in calling code has following issues:但是,有些人认为在调用代码中直接使用 ORM 存在以下问题:

  1. It makes code little more complicated due to lack of separation of concerns.由于缺乏关注点分离,它使代码变得更加复杂。
  2. Data access code is merged in business logic.数据访问代码合并到业务逻辑中。 As a result, redundant complex query logic spread at multiple places;结果,冗余的复杂查询逻辑分散在多个地方; hard to manage.很难管理。
  3. As many ORM objects are used in-line in calling code, it is very hard to unit test the code.由于在调用代码中内嵌使用了许多 ORM 对象,因此很难对代码进行单元测试。
  4. As ORM only exposes Generic Repository, it causes many issues mentioned below.由于 ORM 仅公开通用存储库,因此会导致下面提到的许多问题。

Apart from all above, one other issue generally discussed is "What if we decide to change ORM in future".除上述之外,另一个普遍讨论的问题是“如果我们决定将来更改 ORM 会怎样”。 This should not be key point while taking decision because:这不应该是做出决定时的关键点,因为:

  • You rarely change ORM, mostly NEVER – YAGNI.你很少改变 ORM,大多是从不——YAGNI。
  • If you change ORM, you have to do huge changes anyway.如果您更改 ORM,无论如何您都必须进行巨大的更改。 You may minimize efforts by encapsulating complete data access code (NOT just ORM) inside something .您可以通过将完整的数据访问代码(不仅仅是 ORM)封装在某个东西中来最大限度地减少工作量。 We will discuss that something below.我们将在下面讨论的东西

Considering four issues mentioned above, it may be necessary to create Repositories even though you are using full ORM - This is per case decision though .考虑到上面提到的四个问题,即使您使用的是完整的 ORM,也可能需要创建存储库 - 尽管这是每个案例的决定

Even in that case, Generic Repository must be avoided.即使在这种情况下,也必须避免使用通用存储库。 It is considered an anti-pattern.它被认为是一种反模式。

Why generic repository is anti-pattern?为什么通用存储库是反模式的?

  1. A repository is a part of the domain being modeled, and that domain is not generic.存储库是正在建模的域的一部分,并且该域不是通用的。
    • Not every entity can be deleted.并非每个实体都可以删除。
    • Not every entity can be added并非每个实体都可以添加
    • Not every entity has a repository.并非每个实体都有一个存储库。
    • Queries vary wildly;查询千差万别; the repository API becomes as unique as the entity itself.存储库 API 变得与实体本身一样独特。
    • For GetById() , identifier types may be different.对于GetById() ,标识符类型可能不同。
    • Updating specific fields (DML) not possible.无法更新特定字段 (DML)。
  2. Generic query mechanism is the responsibility of an ORM.通用查询机制是 ORM 的职责。
    • Most of the ORMs expose an implementation that closely resemble with Generic Repository.大多数 ORM 公开了一个与通用存储库非常相似的实现。
    • Repositories should be implementing the SPECIFIC queries for entities by using the generic query mechanism exposed by ORM.存储库应该使用 ORM 公开的通用查询机制来实现实体的 SPECIFIC 查询。
  3. Working with composite keys is not possible.无法使用复合键。
  4. It leaks DAL logic in Services anyway.无论如何,它会泄漏服务中的 DAL 逻辑。
    • Predicate criteria if you accept as parameter needs to be provided from Service layer.如果您接受作为参数的谓词标准,则需要从服务层提供。 If this is ORM specific class, it leaks ORM into Services.如果这是 ORM 特定的类,它会将 ORM 泄漏到服务中。

I suggest you read these ( 1 , 2 , 3 , 4 , 5 ) articles explaining why generic repository is an anti-pattern.我建议您阅读这些( 1 , 2 , 3 , 4 , 5 )文章,这些文章解释了为什么通用存储库是一种反模式。 This other answer discusses about Repository Pattern in general.这个另一个答案一般讨论了存储库模式。

So, I will suggest:所以,我会建议:

  • Do NOT use repository at all, directly use ORM in your calling code.根本不要使用存储库,直接在调用代码中使用 ORM。
  • If you have to use repository, then do not try to implement everything with Generic Repository.如果您必须使用存储库,那么不要尝试使用 Generic Repository 实现所有内容。
    Instead, optionally create very simple and small Generic Repository as abstract base class.相反,可以选择创建非常简单和小型的通用存储库作为抽象基类。 OR you can use Generic Repository exposed by your ORM as base repository if ORM allows it.或者,如果 ORM 允许,您可以使用 ORM 公开的通用存储库作为基础存储库。
    Implement Concrete Repositories as per your need and derive all them from Generic Repository.根据您的需要实施具体存储库,并从通用存储库中派生所有这些。 Expose concrete repositories to calling code.向调用代码公开具体的存储库。
    This way you get all the good of generic repository still bypassing its drawbacks.通过这种方式,您可以在绕过其缺点的情况下获得通用存储库的所有优点。
    Even though very rare, this also helps switching ORM in future as ORM code is cleanly abstracted in DAL/Repositories.尽管非常罕见,但这也有助于将来切换 ORM,因为 ORM 代码在 DAL/Repositories 中被清晰地抽象出来。 Please understand that switching ORM is not a primary objective of Data Access Layer or Repository.请理解切换 ORM 不是数据访问层或存储库的主要目标。

In any case, do not expose Generic Repository to calling code.在任何情况下,不要将通用存储库暴露给调用代码。

Also, do not return IQueryable from concrete repositories.此外,不要从具体存储库返回IQueryable This violates basic purpose of existence of Repositories - To abstract data access.这违反了存储库存在的基本目的 - 抽象数据访问。 With exposing IQueryable outside the repository, many data access decisions leak into calling code and Repository lose the control over it.由于在存储库外部公开IQueryable ,许多数据访问决策泄漏到调用代码中,并且存储库失去了对它的控制。

do I need to create a repository for each entity or implement a generic repository for the context我需要为每个实体创建一个存储库还是为上下文实现一个通用存储库

As suggested above, creating repository for each entity is better approach.如上所述,为每个实体创建存储库是更好的方法。 Note that, Repository should ideally return Domain Model instead of Entity.请注意,理想情况下,存储库应该返回域模型而不是实体。 But this is different topic for discussion.但这是不同的讨论话题。

does a generic repository works for EF Database First?通用存储库是否适用于 EF Database First?

As suggested above, EF itself exposes Generic Repository.如上所述,EF 本身公开了通用存储库。 Building one more layer on it is useless.在它上面再建一层是没有用的。 Your image is saying the same thing.你的图片说的是同样的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM