1 回答
TA贡献1770条经验 获得超3个赞
首先,如果您使用完整的 ORM,如实体框架或 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)文章,这些文章解释了为什么通用存储库是一种反模式。这个另一个答案一般讨论了存储库模式。
所以,我会建议:
根本不要使用存储库,直接在调用代码中使用 ORM。
如果您必须使用存储库,则不要尝试使用 Generic Repository 实现所有内容。
相反,可以选择创建非常简单和小型的通用存储库作为抽象基类。或者,如果 ORM 允许,您可以使用 ORM 公开的通用存储库作为基础存储库。
根据您的需要实施具体存储库,并从通用存储库中派生所有这些。向调用代码公开具体的存储库。
通过这种方式,您可以在绕过其缺点的情况下获得通用存储库的所有优点。
尽管非常罕见,但这也有助于将来切换 ORM,因为 ORM 代码在 DAL/Repositories 中被清晰地抽象出来。请理解切换 ORM 不是数据访问层或存储库的主要目标。
在任何情况下,不要将通用存储库暴露给调用代码。
另外,不要IQueryable
从具体的存储库返回。这违反了存储库存在的基本目的 - 抽象数据访问。由于暴露IQueryable
在存储库之外,许多数据访问决策泄漏到调用代码中,并且存储库失去了对它的控制。
我需要为每个实体创建一个存储库还是为上下文实现一个通用存储库
如上所述,为每个实体创建存储库是更好的方法。请注意,理想情况下,存储库应该返回域模型而不是实体。但这是不同的讨论话题。
通用存储库是否适用于 EF Database First?
如上所述,EF 本身公开了通用存储库。在它上面再建一层是没有用的。你的图片说的是同样的事情。
- 1 回答
- 0 关注
- 187 浏览
添加回答
举报