通用存储库使用EF 4.1有什么意义


145

当我深入研究DbContext,DbSet和关联的接口时,我想知道为什么您需要围绕这些实现来实现一个单独的“通用”存储库?

看起来DbContext和IDbSet可以满足您的所有需求,并在DbContext中包含“工作单元”。

我是否在这里错过了一些东西,或者似乎人们无缘无故地喜欢添加另一层依赖。


这是有争议/基于意见的问题。我在这里已经讨论过
阿米特·乔希

Answers:


202

你说的没错 DbContext是工作单元模式IDbSet的实现,也是存储库模式的实现。

当前,存储库非常受欢迎并且被过度使用。每个人之所以使用它们,是因为有数十篇有关为实体框架创建存储库的文章,但没有人实际描述与此决策相关的挑战。

使用存储库的主要原因通常是:

  • 从上层隐藏EF
  • 使代码更易于测试

第一个原因是某种结构上的纯度和很棒的主意,如果您使上层独立于EF,则以后可以切换到其他持久性框架。您在现实世界中看到过几次?这个原因使使用EF变得更加困难,因为您的存储库必须提供很多其他功能,这些功能包装了EF默认情况下允许的功能。

同时包装EF代码可以使您的代码更有条理,并遵循关注点分离规则。对我来说,这可能是存储库和工作单元的唯一真正优势,但是您必须了解,遵循EF的这一规则可能会使您的代码更易于维护和更好地可读,但是在最初创建应用程序的过程中会变得更高,并且对于较小的应用程序,这可能是不必要的复杂性。

第二个原因是部分正确的。EF的最大缺点是刚性架构,几乎无法模拟,因此如果您要对上层进行单元测试,则必须以某种方式包装EF来模拟其实现。但是,这还有许多其他的后果,我在这里描述。

我关注Ayende的博客。如果您曾经使用过NHibernate,那么您可能知道他的文章。这个家伙最近写了几篇文章反对将存储库与NHibernate一起使用,但是NHibernate具有更好的可模拟性。


3
您可以嘲笑,IDbSet也可以在派生上下文中定义自定义接口,仅此而已。一旦您的代码使用ChangeTracker,Entries或其他任何东西,将需要付出很大的努力才能将它们全部包装起来。
Ladislav Mrnka

1
是的EF不是非常注重性能的工具。至少MS有很多机会可以在将来的版本中使其变得更好。
Ladislav Mrnka

2
@chiccodoro:对。但是,一旦您的模拟类公开IQueryable或接受了Expression<>作为内部参数传递给Linq-to-entities查询的参数,您将在模拟组件外部定义具有副作用的逻辑,这些副作用无法通过单元测试进行测试。
2011年

8
如果我在业务层中直接使用DbSet和BdContext,则必须在那里以及在DataLayer项目中引用EntityFramework.dll。仅此一点就告诉我它需要某种包装。
INGO丘壑

2
downvote:不完整-在存储库接口后抽象EF可以使SL和WPF中运行的客户端代码完全相同。
h.alex

21

我在同样的问题上苦苦挣扎,EF层的单元测试的可模拟性很重要。但是我浏览了这篇很棒的文章,其中解释了如何通过确保派生的DbContext实现了通用接口并公开IDbSet而不是DbSet的方式来将EF 4.1 DbContext设置为可模拟的。由于我使用的是数据库优先方法,因为我们的数据库已经存在,所以我只修改了用于生成派生的DbContext的T4模板,以生成它以返回IDbSet接口以及从我的通用接口派生。这样一来,整个事情就可以很容易地被嘲笑,并且您不需要实现自己的工作单元或存储库模式。只需编写服务代码以使用您的通用接口,然后在进行单元测试时,

http://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-with-a-generic-repository/


5

创建存储库的原因之一是,如果您决定从EntityFramework迁移到其他对象,则可以隐藏DBSet和DbContext的实现,反之亦然。

例如,我正在使用NHibernate,并将所有对该框架的调用都包装在我的存储库类中。他们返回IEnumerable以获得“通用”,并且我的存储库具有标准的CRUD操作(更新,删除等)。我已经搬到实体框架很久了。这样做之后,我不需要更改ViewModel类中的任何内容或其他内容,因为它们指向了我的存储库-我只需要更改存储库的内部即可。这使迁移时的生活更加轻松。

(我之所以使用NHibernate,是因为我们正在连接到ISeries,当时,在ISeries上使用EF并没有影响成本的实现。唯一的可用方法是向IBM支付$ 12,000的DB2Connect费用。)


“几乎”(关于隐藏DBSet和DbContext的主题)将发现您不需要将EF公开给任何使用者(例如,如果您利用DI),但是确实需要一个公开IDbSet <T>属性或再往前走一步,而是将所有属性键入IQueryable <T>,但是我的观点是,您可以完全隐藏对DbSet和DbContext的依赖。然后可以将CRUD ops编写为扩展方法,您可以为不同的后备存储编写多个扩展方法。但是,您不会隐藏LINQ的使用。
肖恩·威尔逊
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.