在使用 ASP.NET MVC 和 ORM 解决方案时,我们是不是需要使用存储库模式?

Posted

技术标签:

【中文标题】在使用 ASP.NET MVC 和 ORM 解决方案时,我们是不是需要使用存储库模式?【英文标题】:Do we need to use the Repository pattern when working in ASP.NET MVC with ORM solutions?在使用 ASP.NET MVC 和 ORM 解决方案时,我们是否需要使用存储库模式? 【发布时间】:2011-06-11 02:16:07 【问题描述】:

我有点好奇其他开发人员在使用 Entity Framework 或 NHibernate 在 ASP.NET MVC 中进行编程时应用存储库模式的经验。在我看来,这种模式已经在 ORM 本身中实现了。实体框架中的 DbContextDbSet<T> 以及 NHibernate 中的 ISessionRepository 模式中提到的大多数问题——在POEE 和DDD 中进行了分类——这些 ORM 都很好地实现了。也就是说,这些担忧是,

坚持 OO 查看数据 数据访问逻辑抽象 查询访问逻辑

此外,我所见过的大多数存储库模式的实现都遵循这种实现模式——假设我们正在开发一个博客应用程序。

NHibernate 实现:

public class PostRepository : IPostRepository

    private ISession _session;

    public PostRepository(ISession session)
    
        _session = session;
    

    public void Add(Post post)
    
        _session.Save(post);
    

    // other crud methods. 

实体框架:

public class PostRepository : IPostRepository

    private DbContext _session;

    public PostRepository(DbContext session)
    
        _session = session;
    

    public void Add(Post post)
    
        _session.Posts.Add(post);
        -session.SaveChanges();
    

    // other crud methods. 

在我看来,当我们使用 ORM(例如 Nhibernate 或实体框架)时,创建这些存储库实现是多余的。此外,由于这些模式的实现并不比 ORMS 中已有的更多,因此它们更像是噪音,而不是有用的 OO 抽象。似乎在上述情况下使用存储库模式无非是开发人员自我强化和更多的排场和仪式,没有任何可实现的技术优势。你有什么想法??

【问题讨论】:

我同意这里的观点(和I'm not the only one),但是“你的想法是什么”是一个主观和有争议的问题的定义。 "创建这些存储库实现是多余的" 那是因为他们做错了。存储库应该代表概念,而不是通用的CRUD。 @Jeff - 就其本质而言,“软件模式”对上下文非常敏感,并且不像编程语言和平台那样通用。阅读此blog.plover.com/2006/09/11。因此,在讨论软件模式时,不可避免地会有某种程度的主观理由。 @qstarin - Repository 的软件模式规范声明Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects. 显然,Repository 模式的构思是为了抽象出数据访问问题,像 nHibernate 和 Entity Framework 这样的 ORM 做得非常好。在 DDD 中,域对象抽象域逻辑而不是存储库。这使得存储库模式变得多余。 据我所知,ORM 中缺少的存储库模式的主要有形好处是能够抽象依赖关系(测试)并将可重用查询保存在一个地方。在我看来,这些缺点可以而且应该由 ORM 设计者解决。事实上,在代码生成和扩展方法的部分类之间,我认为查询重用问题已经被实体框架解决了一些。 【参考方案1】:

如果您不需要能够切换 ORM 或能够测试任何依赖于您的 ORM/数据库的类,答案是

如果您希望能够切换 ORM 或能够轻松测试使用数据库层的类:是的,您需要一个存储库(带有接口规范)。

如果您使用存储库模式,您还可以切换到内存存储库(我在单元测试中这样做)、XML 文件或其他任何内容。

更新

您可以通过谷歌搜索找到的大多数存储库模式实现的问题是它们在生产环境中效果不佳。他们缺乏限制结果(分页)和排序结果的选项,这真是太棒了。

存储库模式在与 UnitOfWork 实现相结合并支持规范模式时大放异彩。

如果你发现有一个拥有所有这些,请告诉我 :)(我有我自己的,但运行良好的规范部分除外)

更新 2

存储库不仅仅是以抽象的方式访问数据库,例如可以通过 ORM 完成。一个普通的存储库实现应该处理所有聚合实体(例如OrderOrderLine)。如果在同一个存储库类中处理它们,您始终可以确保它们构建正确。

但是,嘿,你说:这是 ORM 自动为我完成的。嗯,是的,也不是。如果您创建一个网站,您很可能只想编辑一个订单行。您是否获取完整的订单,循环查找订单,然后将其添加到视图中?

通过这样做,您将向控制器引入不属于那里的逻辑。当一个网络服务想要同样的东西时,你怎么做?复制你的代码?

通过使用 ORM,很容易从任何地方获取任何实体myOrm.Fetch<User>(user => user.Id == 1) 修改它然后保存它。这可能非常方便,但也会增加代码异味,因为您复制代码并且无法控制对象的创建方式,如果它们具有有效的状态或正确的关联。

接下来想到的是,您可能希望能够以集中方式订阅 Created、Updated 和 Deleted 等事件。如果您有存储库,这很容易。

对我来说,ORM 提供了一种将类映射到表的方法,仅此而已。我仍然喜欢将它们包装在存储库中以控制它们并获得单点修改。

【讨论】:

@Bikal Gurung:或者用更激烈的话说:存储库不是必需的,但它可以让您轻松地将一个 orm 替换为另一个。 实际上,我会强调存储库模式使测试代码变得更容易。这比替换 ORM 实现更重要。另外,请检查我的更新答案。 @k3b - 这是一个糟糕的理由。动态切换 ORM 很少见。 @jgauffin - 我不明白你的更新。如果一个仓库是在没有分页或排序的情况下构建的,那么它就没有它。它与模式本身无关。 我在更新中确实说过实现,并不是说它是模式本身的问题。 @Bikal 好处实际上并不能改变 ORM,它比这更微妙。您可以更改它的事实表明您已成功地将域层与数据访问问题分离,从而为您提供松散耦合、内聚的设计。这意味着领域层只负责对领域进行建模。这更容易测试、更容易重构,以及 DDD 的所有其他优点。【参考方案2】:

我认为只有当你想降低依赖程度时才有意义。在抽象中,您可以在您的基础设施包中拥有 IPostRepository,并在 EF 或 NH 或其他东西之上构建此接口的几个独立实现。它对 TDD 很有用。

在实践中,NH 会话(和 EF 上下文)实现了类似于“工作单元”模式的东西。此外,使用 NH 和存储库模式,您可能会遇到很多错误和架构问题。

例如,可以绕过存储库实现来保存 NH 实体。您可以从 session (Repository.Load) 中获取它,更改其属性之一,然后调用 session.Flush(例如,在请求结束时,因为 Repository 模式不假设刷新) - 您的更改将在分贝。

【讨论】:

【参考方案3】:

您只提到了基本的 CRUD 操作。直接执行这些操作确实意味着您必须了解存储库可以打包的事务、刷新和其他事情,但我想当您考虑复杂的检索查询时,存储库的价值会变得更加明显。

然后想象一下,您决定直接在应用程序层中使用 NHibernate 会话。

您将需要使用 HQL 或 NHibernate 标准执行与 WHERE 子句和 ORDER BY 等等效的操作。这意味着您的代码必须引用 NHibernate,并包含特定于 NHibernate 的想法。这使您的应用程序难以测试,并且让不熟悉 NH 的其他人更难以跟进。对 repository.GetCompletedOrders 的调用比包含“where IsComplete = true and IsDeleted = false...”等内容的调用更具描述性和可重用性。

您可以改用 Linq to NHibernate,但现在您很容易忘记您正在处理 IQueryable。您最终可能会链接 Linq 表达式,这些表达式在执行时会生成大量查询,而您却没有意识到这一点(我是根据经验说话)! Mike Hadlow 在他的帖子Should my repository expose IQueryable 中引发了关于这个主题的对话。

注意如果您不喜欢在自定义存储库中为不同的查询(例如 GetCompletedOrders)提供大量方法,您可以使用规范参数(例如 Get(specification)),它允许您在不使用数据访问语言的情况下指定过滤器、排序等。

回到您提供的存储库的好处列表:

坚持 OO 查看数据 数据访问逻辑抽象 查询访问逻辑

您可以看到,直接使用持久性框架类并没有提供第 3 点和第 4 点,尤其是在现实世界的检索场景中。

【讨论】:

以上是关于在使用 ASP.NET MVC 和 ORM 解决方案时,我们是不是需要使用存储库模式?的主要内容,如果未能解决你的问题,请参考以下文章

带有 Postgres 的 ASP.NET MVC; ORM 推荐?

使用 ASP.Net MVC 对网格进行分页和排序

ASP.NET MVC 使用 Petapoco 微型ORM框架+NpgSql驱动连接 PostgreSQL数据库

新的 MVC 4 项目基于使用 Subsonic 作为 ORM 的 ASP.net 网络论坛应用程序......从哪里开始......?

.net、asp.net、MVC、Ado.Net、EF、ORM、NHibemate之间的关系?

[ASP.NET MVC]: - EF框架学习手记