在使用 ASP.NET MVC 和 ORM 解决方案时,我们是否需要使用存储库模式?
我有点好奇其他开发人员在使用 Entity Framework 或 NHibernate 在 ASP.NET MVC 中进行编程时应用存储库模式的经验。在我看来,这种模式已经在 ORM 本身中实现了。实体框架中的 DbContext
和 DbSet
以及 ISession
。
Repository
模式中提到的大多数问题 - 如 POEE 和 DDD - 这些 ORM 已经相当充分地实现了。即这些关注点是:
- 持久性
- 面向对象的数据视图 数据
- 访问逻辑 抽象
- 查询访问逻辑
此外,我见过的大多数存储库模式的实现都遵循此实现模式 - 假设我们正在开发一个博客应用程序。
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 am bit curious as to what experience other developers have of applying the Repository pattern when programming in ASP.NET MVC with Entity Framework or NHibernate. It seems to me that this pattern is already implemented in the ORM themselves. DbContext
and DbSet<T>
in the Entity Framework and by the ISession
in NHibernate. Most of the concerns mentioned in the Repository
pattern - as catalogued in POEE and DDD - are pretty adequately implemented by these ORMs. Namely these concerns are,
- Persistence
- OO View of the data
- Data Access Logic Abstraction
- Query Access Logic
In addition, most of the implemententations of the repository pattern that I have seen follow this implementation pattern - assuming that we are developing a blog application.
NHibernate implementation:
public class PostRepository : IPostRepository
{
private ISession _session;
public PostRepository(ISession session)
{
_session = session;
}
public void Add(Post post)
{
_session.Save(post);
}
// other crud methods.
}
Entity Framework:
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.
}
It seems to me that when we are using ORMs - such as Nhibernate or Entity Framework - creating these repository implementation are redundant. Furthermore since these pattern implementations does no more than what is already there in the ORMS, these act more as noise than helpful OO abstractions. It seems using the repository pattern in the situation mentioned above is nothing more than developer self aggrandizement and more pomp and ceremony without any realizable techical benefits. What are your thoughts ??
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您不需要能够切换 ORM 或能够测试任何依赖于您的 ORM/数据库的类,答案是否。
如果您希望能够切换 ORM 或能够轻松测试使用数据库层的类:是的,您需要一个存储库(带有接口规范)。
如果您使用存储库模式,您还可以切换到内存存储库(我在单元测试中这样做)、XML 文件或其他任何内容。
更新
通过谷歌搜索可以找到的大多数存储库模式实现的问题是它们在生产中不能很好地工作。他们缺乏限制结果(分页)和对结果进行排序的选项,这有点令人惊奇。
当存储库模式与 UnitOfWork 实现相结合并支持规范模式时,它就会发挥它的光彩。
如果您发现一个具有所有这些功能,请告诉我:)(我确实有自己的,但工作良好的规范部分除外)
更新 2
存储库不仅仅是访问数据库中的数据库抽象的方式,例如可以通过 ORM 来完成。正常的存储库实现应该处理所有聚合实体(例如
Order
和OrderLine
)。如果在同一个存储库类中处理它们,您始终可以确保它们正确构建。但是,嘿,你会说:这是 ORM 自动为我完成的。嗯,是的,也不是。如果您创建一个网站,您很可能只想编辑一个订单行。您是否获取完整的订单,循环遍历以找到订单,然后将其添加到视图中?
通过这样做,您可以向控制器引入不属于那里的逻辑。当网络服务需要同样的东西时,你该怎么做?重复您的代码?
通过使用 ORM,可以很容易地从任何地方获取任何实体
myOrm.Fetch(user => user.Id == 1)
修改它然后保存它。这可能非常方便,但也会增加代码味道,因为您重复代码并且无法控制对象的创建方式(如果它们具有有效的状态或正确的关联)。接下来想到的是,您可能希望能够以集中的方式订阅“创建”、“更新”和“删除”等事件。如果您有存储库,这很容易。
对我来说,ORM 提供了一种将类映射到表的方法,仅此而已。我仍然喜欢将它们包装在存储库中以控制它们并获得单点修改。
The answer is no if you do not need to be able to switch ORM or be able to test any class that has a dependency to your ORM/database.
If you want to be able to switch ORM or be able to easily test your classes which uses the database layer: Yes you need a repository (with an interface specification).
You can also switch to a memory repository (which I do in my unit tests), a XML file or whatever if you use repository pattern.
Update
The problem with most repository pattern implementations which you can find by Googling is that they don't work very well in production. They lack options to limit the result (paging) and ordering the result which is kind of amazing.
Repository pattern comes to it's glory when it's combined with a UnitOfWork implementation and has support for the Specification pattern.
If you find one having all of that, let me know :) (I do have my own, exception for a well working specification part)
Update 2
Repository is so much more than just accessing the database in a abstracted way such as can be done by ORM's. A normal Repository implementation should handle all aggregate entities (for instance
Order
andOrderLine
). Bu handling them in the same repository class you can always make sure that those are built correctly.But hey you say: That's done automatically for me by the ORM. Well, yes and no. If you create a website, you most likely want to edit only one order line. Do you fetch the complete order, loop through it to find the order, and then add it to the view?
By doing so you introduce logic to your controller that do not belong there. How do you do it when a webservice want's the same thing? Duplicate your code?
By using a ORM it's quite easy to fetch any entity from anywhere
myOrm.Fetch<User>(user => user.Id == 1)
modify it and then save it. This can be quite handy, but also add code smells since you duplicate code and have no control over how the objects are created, if they got a valid state or correct associations.The next thing that comes to mind is that you might want to be able to subscribe on events like Created, Updated and Deleted in a centralized way. That's easy if you have a repository.
For me an ORM provides a way to map classes to tables and nothing more. I still like to wrap them in repositories to have control over them and get a single point of modification.
我认为只有当你想降低依赖程度时才有意义。抽象地说,您可以在基础设施包中包含 IPostRepository,并在 EF 或 NH 或其他东西之上构建此接口的多个独立实现。它对于 TDD 很有用。
在实践中,NH 会话(和 EF 上下文)实现了类似“工作单元”模式的东西。此外,使用 NH 和存储库模式,您可能会遇到很多错误和架构问题。
例如,可以绕过存储库实现来保存 NH 实体。您可以从会话(Repository.Load)获取它,更改其属性之一,然后调用 session.Flush (例如,在请求结束时,因为存储库模式不假定刷新) - 并且您的更改将在数据库。
I think it make sense only if you want to decrease level of dependency. In the abstract you can have IPostRepository in your infrastructure package and several independent implementations of this interface built on top of EF or NH, or something else. It useful for TDD.
In practice NH session (and EF context) implements something like the "Unit of Work" pattern. Furthermore with NH and the Repository pattern you can get a lot of bugs and architectural issues.
For example, NH entity can be saved bypassing your Repository implementation. You can get it from session (Repository.Load), change one of its properties, and call session.Flush (at the end of request for example, because Repository pattern doesn't suppose flushing) - and your changes will be successfully processed in db.
您只提到了基本的 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 在他的帖子 应该我的存储库公开了 IQueryable。
注意:如果您不喜欢在自定义存储库上针对不同查询使用大量方法(例如 GetCompletedOrders),则可以使用规范参数(例如 Get(specification)),它允许您指定过滤器、排序等,而无需使用数据访问语言。
回到您给出的存储库的好处列表:
您可以看到直接使用持久性框架类并没有提供第 3 点和第 4 点,尤其是在现实世界中检索场景。
You've only mentioned basic CRUD actions. Doing these directly does mean you have to be aware of transactions, flushing and other things that a repository can wrap up, but I guess the value of repositories becomes more apparent when you think about complex retrieval queries.
Imagine then that you do decide to use the NHibernate session directly in your application layer.
You will need to do the equivalent of WHERE clauses and ORDER BYs etc, using either HQL or NHibernate criteria. This means your code has to reference NHibernate, and contains ideas specific to NHibernate. This makes your application hard to test and harder for others unfamiliar with NH to follow. A call to repository.GetCompletedOrders is much more descriptive and reusable than one that includes something like "where IsComplete = true and IsDeleted = false..." etc.
You could use Linq to NHibernate instead, but now you have the situation where you can easily forget that you're working on an IQueryable. You could end up chaining Linq expressions which generate enormous queries when they execute, without realising it (I speak from experience)! Mike Hadlow sparked a conversation on essentially this topic in his post Should my repository expose IQueryable.
N.b. If you don't like having lots of methods on custom repositories for different queries (like GetCompletedOrders), you can use specification parameters (like Get(specification)), which allow you to specify filters, orderings etc. without using data access language.
Going back to the list of benefits of repository that you gave:
You can see that points 3 and 4 are not provided for by using the persistence framework classes directly, especially in real world retrieval scenarios.