Answers:
你说的没错 DbContext
是工作单元模式IDbSet
的实现,也是存储库模式的实现。
当前,存储库非常受欢迎并且被过度使用。每个人之所以使用它们,是因为有数十篇有关为实体框架创建存储库的文章,但没有人实际描述与此决策相关的挑战。
使用存储库的主要原因通常是:
第一个原因是某种结构上的纯度和很棒的主意,如果您使上层独立于EF,则以后可以切换到其他持久性框架。您在现实世界中看到过几次?这个原因使使用EF变得更加困难,因为您的存储库必须提供很多其他功能,这些功能包装了EF默认情况下允许的功能。
同时包装EF代码可以使您的代码更有条理,并遵循关注点分离规则。对我来说,这可能是存储库和工作单元的唯一真正优势,但是您必须了解,遵循EF的这一规则可能会使您的代码更易于维护和更好地可读,但是在最初创建应用程序的过程中会变得更高,并且对于较小的应用程序,这可能是不必要的复杂性。
第二个原因是部分正确的。EF的最大缺点是刚性架构,几乎无法模拟,因此如果您要对上层进行单元测试,则必须以某种方式包装EF来模拟其实现。但是,这还有许多其他的后果,我在这里描述。
我关注Ayende的博客。如果您曾经使用过NHibernate,那么您可能知道他的文章。这个家伙最近写了几篇文章反对将存储库与NHibernate一起使用,但是NHibernate具有更好的可模拟性。
IDbSet
也可以在派生上下文中定义自定义接口,仅此而已。一旦您的代码使用ChangeTracker,Entries或其他任何东西,将需要付出很大的努力才能将它们全部包装起来。
IQueryable
或接受了Expression<>
作为内部参数传递给Linq-to-entities查询的参数,您将在模拟组件外部定义具有副作用的逻辑,这些副作用无法通过单元测试进行测试。
我在同样的问题上苦苦挣扎,EF层的单元测试的可模拟性很重要。但是我浏览了这篇很棒的文章,其中解释了如何通过确保派生的DbContext实现了通用接口并公开IDbSet而不是DbSet的方式来将EF 4.1 DbContext设置为可模拟的。由于我使用的是数据库优先方法,因为我们的数据库已经存在,所以我只修改了用于生成派生的DbContext的T4模板,以生成它以返回IDbSet接口以及从我的通用接口派生。这样一来,整个事情就可以很容易地被嘲笑,并且您不需要实现自己的工作单元或存储库模式。只需编写服务代码以使用您的通用接口,然后在进行单元测试时,
创建存储库的原因之一是,如果您决定从EntityFramework迁移到其他对象,则可以隐藏DBSet和DbContext的实现,反之亦然。
例如,我正在使用NHibernate,并将所有对该框架的调用都包装在我的存储库类中。他们返回IEnumerable以获得“通用”,并且我的存储库具有标准的CRUD操作(更新,删除等)。我已经搬到实体框架很久了。这样做之后,我不需要更改ViewModel类中的任何内容或其他内容,因为它们指向了我的存储库-我只需要更改存储库的内部即可。这使迁移时的生活更加轻松。
(我之所以使用NHibernate,是因为我们正在连接到ISeries,当时,在ISeries上使用EF并没有影响成本的实现。唯一的可用方法是向IBM支付$ 12,000的DB2Connect费用。)