【问题标题】:Should concrete service depend on concrete repository or interface?具体服务是否应该依赖于具体的存储库或接口?
【发布时间】:2014-05-09 18:20:15
【问题描述】:

在使用 ASP.net MVC 和实体框架时,我一直在努力实现存储库和服务模式。

我了解存储库模式的用途以及它如何帮助测试并使数据层可互换。但是,实体框架已经是一个存储库/工作单元模式。我见过的每个存储库模式的实现要么剥夺了 EF 的有用功能,要么实现了一个 IQueryable<T> 属性,这让纯粹主义者感到不安。

如果我正在为我的服务层编写接口,那么实现这些服务的具体版本是我不应该直接在服务中使用实体框架作为我的数据层的原因(而不是将其包装在存储库中/工作单元模式和注入依赖项)?例如:我不能创建一个IService 接口,然后创建一个具体的EfService(实体框架)或NhService(NHibernate 版本)实现吗?

然后我可以使用我的 IoC 容器注入所需的服务和数据上下文。


这是我根据 Sergey 的 cmets 提出的示例:https://github.com/sweeperq/Monona

这就是该模式的实现方式吗?

【问题讨论】:

    标签: c# entity-framework dependency-injection repository-pattern


    【解决方案1】:

    我总是使用存储库接口。

    第一个原因是可测试性 - DbContextDbSet<T> 很难模拟。因此,即使可能,对服务进行单元测试也变得非常困难。

    第二个原因是 IQueryable<T> 被 EF 暴露。它使数据访问逻辑分布在整个应用程序中,这违反了 SRP 并使修复问题变得更加困难。

    第三个原因可能不是很重要 - 切换接口实现的能力 - 不仅适用于真实对象和模拟,而且适用于不同的数据访问框架。你总是想——“不,我肯定会坚持使用 EF”。但在现实世界中,由于性能问题和易于开发,我有切换到 Dapper 的经验。一旦我有从内存数据存储切换到 EF 的经验。因此,拥有该选项而不是依赖于特定于框架的 API 总是很好的。

    第四个原因是存储库为您提供了很好的特定于域的 API。而不是像db.Orders.Where(o => o.Category == "Food") 这样的东西,你有很好的方法repository.GetOrdersByCategory

    另一个原因是您在一个地方有数据访问逻辑。没有重复。在上面的示例中,您的应用程序中可以有五个位置,它们使用相同的查询按类别过滤订单。使用存储库,您将拥有单一的方法,将在五个地方调用。考虑一下如果您的业务规则会发生变化怎么办?例如。您还需要检查类别是否处于活动状态。

    另外请记住 - 一旦您拥有 UoW 和 Repository 基类和接口,您就可以在不同的项目中重用它们。

    【讨论】:

    • Sergey,你为什么要模拟 DbContextDbSet<T>?你不能只是嘲笑服务吗?如果我显得幼稚,请原谅我。我试图理解这一点很长时间了,但从来没有理解过它的目的。希望清楚。
    • @Sam 我说的是服务(或其他依赖于存储库的类)的单元测试
    • 你有什么有用的例子可以指点我吗?我正在努力了解存储库如何使您的服务更容易进行单元测试。
    • @Sam 您是否尝试过对依赖 EF 的服务进行单元测试? :) 如果您在模拟存储库接口时遇到问题,那么您可能正在从存储库返回 IQueryable。切换到IEnumerable,你会很开心
    • @Sam 是的,没错。这是 UoW 和存储库的最简单实现,可以使用。通常,您还会在存储库接口中使用方法IEnumerable<T> GetAll()。如果您有大量实体(即数据库中的表),那么您可以使用反射来实例化 UoW 中的存储库。类似于通用方法GetRepository<T>。但我建议使用最简单的实现作为开始,就像你指出的那样
    猜你喜欢
    • 2011-11-10
    • 1970-01-01
    • 2023-04-06
    • 2014-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多