每个实体的通用存储库或特定存储库?
Generic Repository or Specific Repository for each entity?
背景
在我工作的公司,我被命令更新旧的 MVC 应用程序并为 SQL 数据库实施存储库模式。我使用 Entity Framework Database-First 创建了数据库的上下文并获得了 23 个实体。
第一题
我是否需要为每个实体创建一个存储库或为上下文实现一个通用存储库?我问这个是因为我在搜索互联网时发现了以下内容:
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:
public class VegaRepository {}
Instead, you should have a separate repository per domain class, like OrderRepository, ShippingRepository and ProductRepository.
Source: Programming with Mosh: 4 Common Mistakes with the Repository Pattern
第二题
通用存储库是否适用于 Entity Framework 数据库优先?这是因为我在网上搜索时发现了以下内容:
Entity framework
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,如 Entity Framework 或 NHibernate,您应该避免实施额外的存储库层和工作单元。
这是因为; ORM 本身公开了通用存储库和工作单元。
对于 EF,您的 DbContext
是工作单元,DbSet
是通用存储库。在 NHibernate 的情况下,它是 ISession
本身。
在相同的现有包装器上构建通用存储库的新包装器是重复工作。为什么要重新发明轮子?
但是,有人认为在调用代码中直接使用ORM有以下问题:
- 由于缺乏关注点分离,它使代码稍微复杂一些。
- 数据访问代码合并到业务逻辑中。结果,冗余的复杂查询逻辑散布在多个地方;难以管理。
- 由于在调用代码中内联使用了许多 ORM 对象,因此很难对代码进行单元测试。
- 由于 ORM 仅公开通用存储库,因此会导致下面提到的许多问题。
除上述所有内容外,另一个普遍讨论的问题是"What if we decide to change ORM in future"。这不应该是做决定时的关键点,因为:
- 您很少更改 ORM,大多数时候从不更改 – YAGNI。
- 如果您更改 ORM,则无论如何都必须进行巨大的更改。您可以通过将完整的数据访问代码(不仅仅是 ORM)封装在 something 中来最大限度地减少工作量。我们将在下面讨论某事。
考虑到上面提到的四个问题,即使您使用的是完整的 ORM,也可能需要创建存储库 - 这是根据具体情况决定的。
即使在那种情况下,也必须避免使用 Generic Repository。它被认为是一种反模式。
为什么通用存储库是反模式?
- 存储库是正在建模的域的一部分,并且该域不是通用的。
- 并非每个实体都可以删除。
- 并非每个实体都可以添加
- 并非每个实体都有存储库。
- 查询千差万别;存储库 API 变得与实体本身一样独特。
- 对于
GetById()
,标识符类型可能不同。
- 无法更新特定字段 (DML)。
- 通用查询机制是 ORM 的责任。
- 大多数 ORM 公开了与通用存储库非常相似的实现。
- 存储库应该使用 ORM 公开的通用查询机制来实现对实体的特定查询。
- 无法使用复合键。
- 它无论如何都会泄露服务中的 DAL 逻辑。
- 谓词标准如果你接受作为参数需要从服务层提供。如果这是 ORM 特定的 class,它会将 ORM 泄漏到服务中。
我建议你阅读这些(1, 2, 3, 4, 5) articles explaining why generic repository is an anti-pattern. This 回答一般讨论了存储库模式。
所以,我会建议:
- 根本不要使用存储库,直接在调用代码中使用 ORM。
- 如果您必须使用存储库,请不要尝试使用通用存储库实现所有内容。
相反,可选择创建非常简单和小型的通用存储库作为抽象基础 class。或者,如果 ORM 允许,您可以使用 ORM 公开的通用存储库作为基础存储库。
根据您的需要实施具体存储库,并从通用存储库中派生所有这些存储库。将具体存储库公开给调用代码。
通过这种方式,您可以获得通用存储库的所有好处,同时绕过它的缺点。
尽管非常罕见,但这也有助于将来切换 ORM,因为 ORM 代码在 DAL/Repositories 中被清晰地抽象出来。请理解切换 ORM 不是主要的 objective 数据访问层或存储库。
无论如何,不要将通用存储库暴露给调用代码。
此外,不要 return IQueryable
来自具体的存储库。这违反了存储库存在的基本目的——抽象数据访问。通过在存储库外部公开 IQueryable
,许多数据访问决策会泄漏到调用代码中,存储库将失去对其的控制。
do I need to create a repository for each entity or implement a generic repository for the context
如上所述,为每个实体创建存储库是更好的方法。请注意,理想情况下,存储库应该 return 领域模型而不是实体。但这是不同的讨论话题。
does a generic repository works for EF Database First?
如上所述,EF 本身公开了 Generic Repository。在上面再盖一层也没用。你的图片也在表达同样的意思。
背景
在我工作的公司,我被命令更新旧的 MVC 应用程序并为 SQL 数据库实施存储库模式。我使用 Entity Framework Database-First 创建了数据库的上下文并获得了 23 个实体。
第一题
我是否需要为每个实体创建一个存储库或为上下文实现一个通用存储库?我问这个是因为我在搜索互联网时发现了以下内容:
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:
public class VegaRepository {}
Instead, you should have a separate repository per domain class, like OrderRepository, ShippingRepository and ProductRepository.
Source: Programming with Mosh: 4 Common Mistakes with the Repository Pattern
第二题
通用存储库是否适用于 Entity Framework 数据库优先?这是因为我在网上搜索时发现了以下内容:
Entity framework
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,如 Entity Framework 或 NHibernate,您应该避免实施额外的存储库层和工作单元。
这是因为; ORM 本身公开了通用存储库和工作单元。
对于 EF,您的 DbContext
是工作单元,DbSet
是通用存储库。在 NHibernate 的情况下,它是 ISession
本身。
在相同的现有包装器上构建通用存储库的新包装器是重复工作。为什么要重新发明轮子?
但是,有人认为在调用代码中直接使用ORM有以下问题:
- 由于缺乏关注点分离,它使代码稍微复杂一些。
- 数据访问代码合并到业务逻辑中。结果,冗余的复杂查询逻辑散布在多个地方;难以管理。
- 由于在调用代码中内联使用了许多 ORM 对象,因此很难对代码进行单元测试。
- 由于 ORM 仅公开通用存储库,因此会导致下面提到的许多问题。
除上述所有内容外,另一个普遍讨论的问题是"What if we decide to change ORM in future"。这不应该是做决定时的关键点,因为:
- 您很少更改 ORM,大多数时候从不更改 – YAGNI。
- 如果您更改 ORM,则无论如何都必须进行巨大的更改。您可以通过将完整的数据访问代码(不仅仅是 ORM)封装在 something 中来最大限度地减少工作量。我们将在下面讨论某事。
考虑到上面提到的四个问题,即使您使用的是完整的 ORM,也可能需要创建存储库 - 这是根据具体情况决定的。
即使在那种情况下,也必须避免使用 Generic Repository。它被认为是一种反模式。
为什么通用存储库是反模式?
- 存储库是正在建模的域的一部分,并且该域不是通用的。
- 并非每个实体都可以删除。
- 并非每个实体都可以添加
- 并非每个实体都有存储库。
- 查询千差万别;存储库 API 变得与实体本身一样独特。
- 对于
GetById()
,标识符类型可能不同。 - 无法更新特定字段 (DML)。
- 通用查询机制是 ORM 的责任。
- 大多数 ORM 公开了与通用存储库非常相似的实现。
- 存储库应该使用 ORM 公开的通用查询机制来实现对实体的特定查询。
- 无法使用复合键。
- 它无论如何都会泄露服务中的 DAL 逻辑。
- 谓词标准如果你接受作为参数需要从服务层提供。如果这是 ORM 特定的 class,它会将 ORM 泄漏到服务中。
我建议你阅读这些(1, 2, 3, 4, 5) articles explaining why generic repository is an anti-pattern. This
所以,我会建议:
- 根本不要使用存储库,直接在调用代码中使用 ORM。
- 如果您必须使用存储库,请不要尝试使用通用存储库实现所有内容。
相反,可选择创建非常简单和小型的通用存储库作为抽象基础 class。或者,如果 ORM 允许,您可以使用 ORM 公开的通用存储库作为基础存储库。
根据您的需要实施具体存储库,并从通用存储库中派生所有这些存储库。将具体存储库公开给调用代码。
通过这种方式,您可以获得通用存储库的所有好处,同时绕过它的缺点。
尽管非常罕见,但这也有助于将来切换 ORM,因为 ORM 代码在 DAL/Repositories 中被清晰地抽象出来。请理解切换 ORM 不是主要的 objective 数据访问层或存储库。
无论如何,不要将通用存储库暴露给调用代码。
此外,不要 return IQueryable
来自具体的存储库。这违反了存储库存在的基本目的——抽象数据访问。通过在存储库外部公开 IQueryable
,许多数据访问决策会泄漏到调用代码中,存储库将失去对其的控制。
do I need to create a repository for each entity or implement a generic repository for the context
如上所述,为每个实体创建存储库是更好的方法。请注意,理想情况下,存储库应该 return 领域模型而不是实体。但这是不同的讨论话题。
does a generic repository works for EF Database First?
如上所述,EF 本身公开了 Generic Repository。在上面再盖一层也没用。你的图片也在表达同样的意思。