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

时间:2018-08-09 15:55:13

标签: c# entity-framework repository-pattern generic-repository

背景

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

第一个问题

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

  

每个域一个存储库

     

您应该将存储库视为内存中域对象的集合。如果您要构建一个名为Vega的应用程序,则不应具有以下存储库:

     

public class VegaRepository {}

     

来源:Programming with Mosh: 4 Common Mistakes with the Repository Pattern

第二个问题

通用存储库是否适用于实体框架数据库优先?这是因为我在搜索互联网时发现了以下内容:

  

实体框架

     

请注意,只有在先使用代码映射POCO的情况下,存储库模式才有用。否则,您将改为通过实体破坏抽象(=则存储库模式不是很有用)。如果您想为您建立基础,则可以阅读本文。

     

来源:CodeProject: Repository pattern, done right

1 个答案:

答案 0 :(得分:7)

首先,如果您使用的是完整的ORM(例如Entity Framework或NHibernate),则应避免实现存储库和工作单元的附加层。 这是因为; ORM本身公开了通用存储库和工作单元。
对于EF,您的DbContext是工作单位,DbSet是通用存储库。对于NHibernate,它本身就是ISession
在相同的现有存储库上构建通用存储库的新包装是重复的工作。为什么要重新发明轮子?

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

  1. 这使代码更加复杂。
  2. 数据访问代码已合并到业务逻辑中。
  3. 由于许多ORM对象在调用代码中被内联使用,因此很难对代码进行单元测试。
  4. 由于ORM仅公开通用存储库,因此会引起以下许多问题。

除上述所有内容外,其他普遍讨论的问题是“如果我们决定将来更改ORM,该怎么办”。这不是决策时的重点,因为:

  • 您很少更改ORM,大多数情况下从不更改– YAGNI。
  • 如果您更改ORM,则无论如何都必须进行巨大的更改。您可以通过在某物中封装完整的数据访问代码(而不仅仅是ORM)来减少工作量。我们将在下面讨论某事

考虑到上述四个问题,即使您使用的是完整的ORM,也可能有必要创建存储库-这是根据情况决定的

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

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

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

我建议您阅读以下(12345)文章,这些文章解释了为什么通用存储库是反模式。这个other答案通常讨论存储库模式。

所以,我建议:

  • 根本不使用存储库,直接在调用代码中使用ORM。
  • 如果必须使用存储库,则不要尝试使用通用存储库来实现所有功能。
    相反,可以选择创建非常简单和小的通用存储库作为抽象基类。或者,如果ORM允许,可以将ORM公开的通用存储库用作基础存储库。
    根据您的需要实施具体存储库,并从通用存储库中派生所有这些存储库。将具体的存储库公开给调用代码。
    这样,您就可以绕过通用存储库的所有弊端。
    尽管非常罕见,但这也有助于将来切换ORM,因为ORM代码在DAL /存储库中是完全抽象的。请理解,切换ORM并不是数据访问层或存储库的主要目标。

无论如何,请勿将通用存储库暴露给调用代码。

此外,请勿从具体存储库返回IQueryable。这违反了存储库存在的基本目的-抽象数据访问。通过将IQueryable暴露在存储库之外,许多数据访问决策都会泄漏到调用代码中,并且存储库失去了对其的控制。

  

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

如上所述,为每个实体创建存储库是更好的方法。请注意,存储库最好返回域模型而不是实体。但这是另一个讨论的话题。

  

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

如上所述,EF本身公开了通用存储库。在上面再建一层是没有用的。您的图片在说同样的话。