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

3 c# entity-framework repository-pattern generic-repository

背景

在我工作的公司,我被命令更新旧的MVC应用程序并实现SQL数据库的存储库模式.我使用Entity Framework Database-First创建了数据库的上下文,并获得了23个实体.

第一个问题

我是否需要为每个实体创建存储库或为上下文实现通用存储库?我问这个是因为我在搜索互联网时发现了以下内容:

每个域一个存储库

您应该将存储库视为内存中域对象的集合.如果您正在构建名为Vega的应用程序,则不应具有如下所示的存储库:

public class VegaRepository {}

来源:与Mosh一起编程:存储库模式的4个常见错误

第二个问题

通用存储库是否适用于Entity Framework Database-First?这是因为我在搜索互联网时发现了以下内容:

实体框架

请注意,只有在首先使用代码映射的POCO时,存储库模式才有用.否则你只需要用实体打破抽象(=存储库模式不是很有用).如果您想为自己生成基础,可以按照本文进行操作.

来源:CodeProject:存储库模式,做得对

Ami*_*shi 11

首先,如果您使用完整的ORM,如Entity Framework或NHibernate,您应该避免实现额外的存储库和工作单元层.这是因为; ORM本身暴露了通用存储库和工作单元.
在EF的情况下,您DbContext的工作单位DbSet是通用存储库.在NHibernate的情况下,它ISession本身.
在相同的现有存储库上构建Generic Repository的新包装器是重复工作.为什么重新发明轮子?

但是,有些人认为在调用代码中直接使用ORM会产生以下问题:

  1. 它使代码变得更加复杂.
  2. 数据访问代码在业务逻辑中合并.
  3. 由于许多ORM对象在调用代码时在线使用,因此很难对代码进行单元测试.
  4. 由于ORM仅公开Generic Repository,因此会导致下面提到的许多问题.

除了上述所有内容之外,一般性讨论的另一个问题是"如果我们决定在未来改变ORM会怎么样".这不应该是决定时的关键点,因为:

  • 你很少改变ORM,主要是从不 - YAGNI.
  • 如果您更改ORM,则无论如何都必须进行大量更改.您可以通过将完整的数据访问代码(不仅仅是ORM)封装在某些内容中来最小化工作量.我们将讨论的东西下面.

考虑到上面提到的四个问题,即使您正在使用完整的ORM,也可能需要创建存储库 - 这是根据每个案例的决定.

即使在这种情况下,也必须避免使用通用存储库.它被认为是一种反模式.

为什么通用存储库是反模式的?

  1. 存储库是正在建模的域的一部分,并且该域不是通用的.
    • 并非每个实体都可以删除.
    • 并非每个实体都可以添加
    • 并非每个实体都有一个存储库.
    • 查询变化很大; 存储库API变得与实体本身一样唯一.
    • 因为GetById(),标识符类型可能不同.
    • 无法更新特定字段(DML).
  2. 通用查询机制是ORM的责任.
    • 大多数ORM暴露出与Generic Repository非常相似的实现.
    • 存储库应该使用ORM公开的通用查询机制来实现实体的SPECIFIC查询.
  3. 无法使用复合键.
  4. 无论如何,它泄漏了服务中的DAL逻辑.
    • 如果您接受参数,则需要从服务层提供谓词条件.如果这是特定于ORM的类,则会将ORM泄漏到服务中.

我建议你阅读这些(1,2,3,4,5)的文章解释了为什么通用存储库是一个反模式.这回答论述了一般Repository模式.

所以,我会建议:

  • 根本不要使用存储库,直接在您的调用代码中使用ORM.
  • 如果必须使用存储库,则不要尝试使用Generic Repository实现所有内容.
    相反,可选择创建非常简单和小型的通用存储库作为抽象基类.或者,如果ORM允许,您可以使用ORM公开的Generic Repository作为基本存储库.
    根据您的需要实现具体的存储库,并从Generic Repository中获取所有这些存储库.将具体存储库暴露给调用代码.
    通过这种方式,您可以获得通用存储库的所有优点,但仍然可以绕过其缺点.
    即使非常罕见,这也有助于在将来切换ORM,因为ORM代码在DAL /存储库中被完全抽象化.请理解,切换ORM不是数据访问层或存储库的主要目标.

在任何情况下,都不要将Generic Repository暴露给调用代码.

另外,不要IQueryable从具体的存储库返回.这违反了存储库存在的基本目的 - 抽象数据访问.随着IQueryable在存储库外部暴露,许多数据访问决策泄漏到调用代码中,而存储库失去对它的控制.

我是否需要为每个实体创建存储库或为上下文实现通用存储库

如上所述,为每个实体创建存储库是更好的方法.请注意,Repository理想情况下应返回Domain Model而不是Entity.但这是讨论的不同主题.

通用存储库是否适用于EF Database First?

如上所述,EF本身公开了Generic Repository.在它上面再建一层是没用的.你的形象说同样的话.

  • “另外,不要从具体的存储库中返回 IQueryable”当直接使用 DbSet 作为存储库时,如何避免这种情况?“您很少更改 ORM,主要是从不”从 .Net 数据集更改为 Linq2Sql,然后更改为 EntityFramework,现在更改为 EFCore 是我的经验中的真实场景(并且当 ORM 没有到处泄漏时,它的工作量会减少)+1 无论如何这个完整的答案 (4认同)