在使用ORM解决方案的ASP.NET MVC中工作时,是否需要使用Repository模式?

时间:2011-01-18 13:02:19

标签: asp.net-mvc design-patterns orm domain-driven-design

我有点好奇其他开发人员在使用Entity Framework或NHibernate在ASP.NET MVC中编程时应用Repository模式的经验。在我看来,这种模式已经在ORM中实现了。实体框架中的 DbContext DbSet<T> 以及NHibernate中的 ISession Repository 模式中提到的大多数问题(在POEEDDD中编目 - 都非常充分地由这些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或Entity Framework--创建这些存储库实现是多余的。此外,由于这些模式实现不会超过ORMS中已有的模式实现,因此它们比有用的OO抽象更像 noise 。似乎在上述情况下使用存储库模式只不过是开发人员自我扩展和更多的盛大和仪式,没有任何可实现的技术优势。你有什么想法?

3 个答案:

答案 0 :(得分:12)

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

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

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

<强>更新

Googling可以找到的大多数存储库模式实现的问题是它们在生产中不能很好地工作。他们缺乏限制结果(分页)和排序结果的选项,这有点令人惊讶。

当它与UnitOfWork实现结合并且支持规范模式时,存储库模式就会变得光彩夺目。

如果你找到一个拥有所有这些,请告诉我:)(我确实有自己的,例外工作正常的部分)

更新2

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

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

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

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

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

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

答案 1 :(得分:2)

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

在实践中,NH会话(和EF上下文)实现类似“工作单元”模式的东西。此外,使用NH和Repository模式,您可以获得许多错误和架构问题。

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

答案 2 :(得分:1)

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

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

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

您可以将Linq用于NHibernate,但现在您可以轻松忘记您正在使用IQueryable。你最终可能会链接Linq表达式,这些表达式在执行时会生成大量查询,而不会意识到它(我从经验中说)! Mike Hadlow在帖子Should my repository expose IQueryable中基本上引发了关于这个主题的对话。

N.b。如果您不喜欢在不同查询的自定义存储库(如GetCompletedOrders)上使用大量方法,则可以使用规范参数(如Get(规范)),这些参数允许您在不使用数据访问语言的情况下指定过滤器,排序等。

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

  • 持久性
  • OO数据视图
  • 数据访问逻辑抽象
  • 查询访问逻辑

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