问题描述
在我工作的公司,我被要求更新旧的 MVC 应用程序并为 SQL 数据库实现存储库模式.我已经使用 Entity Framework Database-First 创建了数据库的上下文并获得了 23 个实体.
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. I have created the context of the database using Entity Framework Database-First and got 23 entities.
我是否需要为每个实体创建一个存储库或为上下文实现一个通用存储库?我问这个是因为我在搜索互联网时发现了以下内容:
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:
每个域一个存储库
您应该将存储库视为内存中域对象的集合.如果您正在构建一个名为 Vega 的应用程序,您不应该拥有如下所示的存储库:
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:
公共类 VegaRepository {}
相反,您应该为每个域类拥有一个单独的存储库,例如 OrderRepository、ShippingRepository 和 ProductRepository.
Instead, you should have a separate repository per domain class, like OrderRepository, ShippingRepository and ProductRepository.
来源:使用 Mosh 编程:使用 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:
实体框架
请注意,存储库模式仅在您有首先使用代码映射的 POCO 时才有用.否则你只会用实体打破抽象(=存储库模式那时不是很有用).您可以关注这篇文章如果你想为你建立一个基金会.
Do note that the repository pattern is only useful if you have POCOs which are mapped using code first. 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.
推荐答案
首先,如果您使用完整的 ORM,如实体框架或 NHibernate,您应该避免实现额外的存储库和工作单元层.这是因为;ORM 本身公开了通用存储库和工作单元.
对于 EF,您的 DbContext
是工作单元,DbSet
是通用存储库.在 NHibernate 的情况下,它是 ISession
本身.
在相同的现有存储库上构建新的通用存储库包装器是重复工作.为什么要重新发明轮子?
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.This is because; the ORM itself exposes both Generic Repository and Unit Of Work.
In case of EF, your DbContext
is Unit Of Work and DbSet
is Generic Repository. In case of NHibernate, it is ISession
itself.
Building new wrapper of Generic Repository over same existing one is repeat work. Why reinvent the wheel?
但是,有人认为直接在调用代码中使用 ORM 存在以下问题:
But, some argue that using ORM directly in calling code has following issues:
- 由于缺乏关注点分离,它使代码稍微复杂一些.
- 数据访问代码合并到业务逻辑中.结果,冗余的复杂查询逻辑分散在多个地方;难以管理.
- 由于在调用代码时内嵌使用了许多 ORM 对象,因此很难对代码进行单元测试.
- 由于 ORM 仅公开通用存储库,因此会导致下面提到的许多问题.
除上述之外,通常讨论的另一个问题是如果我们决定将来更改 ORM 会怎样".这不应该是做出决定时的关键点,因为:
Apart from all above, one other issue generally discussed is "What if we decide to change ORM in future". This should not be key point while taking decision because:
- 您很少更改 ORM,主要是从不 – YAGNI.
- 如果你改变 ORM,无论如何你都必须做出巨大的改变.通过将完整的数据访问代码(不仅仅是 ORM)封装在 something 中,您可以最大限度地减少工作量.我们将在下面讨论一些事情.
- You rarely change ORM, mostly NEVER – YAGNI.
- If you change ORM, you have to do huge changes anyway. You may minimize efforts by encapsulating complete data access code (NOT just ORM) inside something. We will discuss that something below.
考虑到上面提到的四个问题,即使您使用的是完整的 ORM,也可能需要创建存储库 - 这是每个案例的决定.
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.
即使在这种情况下,也必须避免使用通用存储库.它被认为是一种反模式.
Even in that case, Generic Repository must be avoided. It is considered an anti-pattern.
为什么通用存储库是反模式的?
- 存储库是正在建模的域的一部分,该域不是通用的.
- 并非每个实体都可以删除.
- 并非每个实体都可以添加
- 并非每个实体都有存储库.
- 查询千差万别;存储库 API 变得与实体本身一样独特.
- 对于
GetById()
,标识符类型可能不同. - 无法更新特定字段 (DML).
- 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.
- For
GetById()
, identifier types may be different. - Updating specific fields (DML) not possible.
- 大多数 ORM 公开的实现与通用存储库非常相似.
- 存储库应该使用 ORM 公开的通用查询机制为实体实现 SPECIFIC 查询.
- 如果您接受作为参数的谓词标准,则需要从服务层提供.如果这是 ORM 特定的类,则会将 ORM 泄漏到服务中.
我建议你阅读这些(1, 2、3、4, 5) 文章解释了为什么通用存储库是一种反模式.这个 other 答案讨论了一般的存储库模式.
I suggest you read these (1, 2, 3, 4, 5) articles explaining why generic repository is an anti-pattern. This other answer discusses about Repository Pattern in general.
所以,我会建议:
- 根本不要使用存储库,直接在调用代码中使用 ORM.
- 如果您必须使用存储库,那么不要尝试使用通用存储库来实现所有内容.
相反,可以选择创建非常简单和小型的通用存储库作为抽象基类.或者,如果 ORM 允许,您可以使用 ORM 公开的通用存储库作为基础存储库.
根据您的需要实施具体存储库,并从通用存储库中派生所有这些.向调用代码公开具体的存储库.
通过这种方式,您可以在绕过其缺点的情况下获得通用存储库的所有优点.
尽管非常罕见,但这也有助于将来切换 ORM,因为 ORM 代码在 DAL/Repositories 中被清晰地抽象出来.请理解,切换 ORM 不是数据访问层或存储库的主要目标.
无论如何,不要将通用存储库暴露给调用代码.
In any case, do not expose Generic Repository to calling code.
另外,不要从具体的存储库中返回 IQueryable
.这违反了存储库存在的基本目的 - 抽象数据访问.由于将 IQueryable
暴露在存储库之外,许多数据访问决策泄漏到调用代码中,并且存储库失去了对它的控制.
Also, do not return IQueryable
from concrete repositories. 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.
我需要为每个实体创建一个存储库还是为上下文实现一个通用存储库
如上所述,为每个实体创建存储库是更好的方法.请注意,理想情况下,存储库应该返回域模型而不是实体.但这是不同的讨论话题.
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.
通用存储库是否适用于 EF Database First?
如上所述,EF 本身公开了通用存储库.在它上面再建一层是没有用的.您的图片表达了同样的意思.
As suggested above, EF itself exposes Generic Repository. Building one more layer on it is useless. Your image is saying the same thing.
这篇关于每个实体的通用存储库或特定存储库?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!