随着我深入研究DbContext,DbSet和相关接口,我想知道为什么你需要围绕这些实现实现一个单独的“通用”存储库?
看起来DbContext和IDbSet可以完成您需要的所有工作,并在DbContext中包含“工作单元”。
我在这里遗漏了什么,或者看起来人们喜欢无缘无故地添加另一层依赖。
答案 0 :(得分:202)
你其实是对的。 DbContext
是工作单元模式的实现,IDbSet
是存储库模式的实现。
存储库目前非常受欢迎且过度使用。每个人都使用它们只是因为有很多关于为实体框架创建存储库的文章,但没有人真正描述与此决策相关的挑战。
使用存储库的主要原因通常是:
第一个原因是某种建筑纯度和好主意,如果你让你的上层独立于EF,你可以在以后切换到其他持久性框架。你在现实世界中看过多少次这样的事情?这个原因使得使用EF变得更加困难,因为您的存储库必须公开许多附加功能,包括EF默认允许的内容。
同时包装EF代码可以使您的代码更好地组织并遵循分离关注规则。对我而言,这可能是存储库和工作单元的唯一真正优势,但您必须了解遵循此规则与EF可能会使您的代码更易于维护和更好的可读性,但在最初的努力创建您的应用程序将更高和对于较小的应用程序,这可能是不必要的复杂性。
第二个原因部分正确。 EF的一大缺点是刚性架构很难被嘲笑,所以如果你想要对上层进行单元测试,你必须以某种方式包装EF以允许模拟它的实现。但这有许多其他后果,我在其中描述here。
我关注Ayende's blog。如果你曾经使用NHibernate,你可能知道他的文章。这家伙最近写了几篇反对在NHibernate中使用存储库的文章,但NHibernate更易于模仿。
答案 1 :(得分:21)
我正在努力解决相同的问题,并且EF层的单元测试的可模仿性很重要。但是我遇到了这篇伟大的文章,它解释了如何通过确保派生的DbContext实现了一个通用接口并公开IDbSet而不是DbSet来设置EF 4.1 DbContext是可模拟的。由于我使用的是Database First方法,因为我们的数据库已经存在,我只是修改了用于生成派生DbContext的T4模板,以生成它以返回IDbSet接口,以及从我的通用接口派生。这样就可以轻松地模拟整个事物,并且您不需要实现自己的工作单元或存储库模式。只需编写您的服务代码来使用您的通用接口,当您进行单元测试时,只需使用特定的测试数据模拟通用接口,您就可以了。
答案 2 :(得分:5)
创建存储库的一个原因是,如果您决定从EntityFramework转移到其他地方,反之亦然,则可以隐藏DBSet和DbContext的实现。
例如,我使用的是NHibernate,并且在我的存储库类中包含了对该框架的所有调用。它们返回IEnumerable,因为它们是“通用的”,我的存储库具有标准的CRUD操作(更新,删除等)。我早就搬到了Entity Framework。这样做,我不需要在我的ViewModel类中更改任何内容,因为它们指向我的存储库 - 我只需要更改我的存储库内部。这使迁移时的生活更加轻松。
(我使用NHibernate是因为我们连接到ISeries,当时使用EF和ISeries没有成本的情感实现。唯一可用的是为IBM的DB2Connect支付12,000美元)