首先,如果您使用的是完整的ORM,例如Entity framework或NHibernate,则应避免实现存储库和工作单元的附加层。这是因为;
ORM本身公开了通用存储库和工作单元。
如果是EF,则您
DbContext是工作单位,并且
DbSet是通用存储库。如果是NHibernate,它
ISession本身就是。
在相同的现有存储库上构建通用存储库的新包装是重复的工作。为什么要重新发明轮子?
但是 ,有人认为直接在调用代码中使用ORM存在以下问题:
- 由于缺乏关注点分离,它使代码稍微复杂一些。
- 数据访问代码在业务逻辑中合并。结果,冗余的复杂查询逻辑散布在多个地方。很难管理。
- 由于许多ORM对象在调用代码中被内联使用,因此很难对代码进行单元测试。
- 由于ORM仅公开通用存储库,因此会引起以下许多问题。
除了上述所有以外,通常讨论的另一个问题是“如果我们决定将来更改ORM,该怎么办”。这不是决策时的重点,因为:
- 您很少更改ORM,大多数情况下从不更改– YAGNI。
- 如果您更改ORM,则无论如何都必须进行巨大的更改。您可以通过将完整的数据访问代码(不只是ORM)封装在 某物 内来最大程度地减少工作量。我们将讨论 的东西 下面。
考虑到上述四个问题,即使您使用的是完整的ORM,也可能有必要创建存储库-但是, 这是根据情况决定的 。
即使在这种情况下,也必须避免使用通用存储库。它被认为是反模式。
为什么通用存储库是反模式的?
- 存储库是要建模的域的一部分,并且该域不是通用的。
- 并非每个实体都可以删除。
- 并非每个实体都可以添加
- 并非每个实体都有一个存储库。
- 查询差异很大。存储库API变得与实体本身一样独特。
- 对于
GetById()
,标识符类型可能不同。 - 无法更新特定字段(DML)。
- 通用查询机制是ORM的职责。
- 大多数ORM都公开了与通用存储库非常相似的实现。
- 存储库应该通过使用ORM公开的通用查询机制为实体实现SPECIFIC查询。
- 无法使用复合键。
- 无论如何,它将泄漏服务中的DAL逻辑。
- 如果您接受作为参数的谓词条件,则需要从服务层提供。如果这是ORM特定的类,则会将ORM泄漏到服务中。
我建议你阅读这些(1,2,3,4,5)的文章解释了为什么通用存储库是一个反模式。这等回答论述了一般Repository模式。
因此,我建议:
- 根本不使用存储库,直接在调用代码中使用ORM。
- 如果必须使用存储库,则不要尝试使用通用存储库来实现所有功能。
相反,可以选择创建非常简单和小的通用存储库作为抽象基类。或者,如果ORM允许,则可以将ORM公开的通用存储库用作基础存储库。
根据您的需要实施具体存储库,并从通用存储库中派生所有这些存储库。将具体存储库公开给调用代码。
这样,您可以获得通用存储库的所有优点,而绕过了它的缺点。
尽管非常罕见,但这也有助于将来切换ORM,因为ORM代码在DAL /存储库中是完全抽象的。请理解,切换ORM不是数据访问层或存储库的主要目标。
无论如何,请勿将通用存储库暴露给调用代码。
另外,请勿
IQueryable从具体存储库中返回。这违反了存储库存在的基本目的-
抽象数据访问。通过
IQueryable在存储库外部进行公开,许多数据访问决策都会泄漏到调用代码中,并且存储库失去了对其的控制。
我需要为每个实体创建一个存储库还是为上下文实现一个通用存储库
如上所述,为每个实体创建存储库是更好的方法。请注意,存储库最好返回域模型而不是实体。但这是另一个讨论的话题。
通用存储库可用于EF Database First吗?
如上所述,EF本身公开了通用存储库。在上面再建一层是没有用的。您的图片在说同样的话。



