将我的 MVC 提升到一个新的水平:DI 和工作单元
我研究过 Nerddinner 和 ContactManager 等更简单的应用程序,以及 Kigg 等更复杂的应用程序。我了解更简单的,现在我想了解更复杂的。
通常,更简单的应用程序在 LINQtoSQL 或实体框架之上具有存储库类和接口(尽可能松散耦合)。控制器调用存储库来执行必要的数据操作。
当我检查 Kigg 或 Oxite 等更复杂的应用程序时,我看到的一种常见模式是引入以下内容(我在这里只触及表面,但我必须从某个地方开始):
- IOC DI(在 Kigg 的情况下为 Unity)
- Web 请求生命周期管理器
- 工作单元
这是我的问题:
我知道,为了真正拥有松散耦合的应用程序,您必须使用 Unity 之类的东西。但似乎当您将 Unity 引入到混合中时,您还必须引入 Web 请求生命周期管理器。这是为什么?为什么像 Nerddinner 这样的示例应用程序没有 Web 请求生命周期管理器?它到底有什么作用?这是 Unity 特有的事情吗?
我注意到的第二个模式是工作单元的引入。同样的问题:为什么 Nerddinner 或 ContactManager 不使用工作单元?相反,这些应用程序使用 Linq2Sql 或实体框架之上的存储库类来执行数据操作。没有任何工作单元的迹象。它到底是什么以及为什么要使用它?
谢谢
下面是 Nerddiner 中dinnerscontroller级别的 DI 示例:
public DinnersController()
: this(new DinnerRepository()) {
}
public DinnersController(IDinnerRepository repository) {
dinnerRepository = repository;
}
那么我是否正确地假设,由于第一个构造函数,控制器“拥有”dinnerrepository,因此它将取决于控制器的生命周期,因为它是在那里声明的?
I have looked at simpler applications like Nerddinner and ContactManager as well as more complicated ones like Kigg. I understand the simpler ones and now I would like to understand the more complex ones.
Usually the simpler applications have repository classes and interfaces (as loosely coupled as they can get) on top of either LINQtoSQL or the Entity Framework. The repositories are called from the controllers to do the necessary data operations.
One common pattern I see when I examine more complicated applications like Kigg or Oxite is the introduction of (I am only scratching the surface here but I have to start somewhere):
- IOC DI (in Kigg's case Unity)
- Web Request Lifetime manager
- Unit of Work
Here are my questions:
I understand that in order to truly have a loosely coupled application you have to use something like Unity. But it also seems like the moment you introduce Unity to the mix you also have to introduce a Web Request Lifetime Manager. Why is that? Why is it that sample applications like Nerddinner do not have a Web Request Lifetime Manager? What exactly does it do? Is it a Unity specific thing?
A second pattern I notice is the introduction of Unit of Work. Again, same question: Why does Nerddinner or ContactManager not use Unit of Work? Instead these applications use the repository classes on top of Linq2Sql or Entity Framework to do the data manipulation. No sign of any Unit of Work. What exactly is it and why should it be used?
Thanks
Below is a example of DI in Nerddiner at the DinnersController level:
public DinnersController()
: this(new DinnerRepository()) {
}
public DinnersController(IDinnerRepository repository) {
dinnerRepository = repository;
}
So am I right to assume that because of the first constructor the controller "owns" the DinnerRepository and it will therefore depend on the lifetime of the controller since it is declared there?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
通过直接使用 Linq-to-SQL,您的控制器拥有对数据上下文的引用。它通常是控制器内部的私有引用,因此是作为其构造的一部分创建的。无需生命周期管理,因为它位于一处。
但是,当您使用 IoC 容器时,您的数据存储库是在控制器外部创建的。由于为您创建对象的 IoC 容器不知道您将如何以及使用创建的对象多长时间,因此引入了生命周期策略。
例如,数据上下文(存储库)通常在 Web 请求开始时创建并在结束时销毁。但是,对于与外部 Web 服务或某些静态映射器(例如记录器)配合使用的组件,无需每次都创建它们。所以你可能想说创建一次(即单一生活方式)。
所有这一切的发生是因为 IoC 容器(如 Unity)被设计为处理多种情况,并且它们不知道您的具体需求。例如,某些应用程序使用“会话”事务,其中 NHibernate(或实体框架可能)可能会在多个页面/Web 请求期间持续。 IoC 容器允许您调整对象的生命周期以满足您的需求。但正如所说,这是有代价的 - 由于没有单一的预定义策略,因此您必须自己选择一个。
为什么 NerdDinner 和其他应用程序不使用更先进的技术,只是因为它们旨在演示 MVC 功能,而不是其他一些库的高级用法。我记得一篇文章是为了演示一个 IoC 容器高级功能而写的 - 这篇文章打破了一些已批准的设计模式,例如关注点分离 - 但这并不重要,因为设计模式不是本文的目标。与简单的 MVC 演示应用程序相同 - 他们不希望您(MVC 新手)迷失在 IoC 迷宫中。
我不建议将 Oxite 作为设计参考示例:
http://codebetter. com/blogs/karlseguin/archive/2008/12/15/oxite-oh-dear-lord-why.aspx
http://ayende. com/Blog/archive/2008/12/19/oxite-open-exchangable-informative-troubled-engine.aspx
With Linq-to-SQL is used directly, your controller owns the reference to the data context. It's usually a private reference inside the controller, and so is created as part of its construction. There's no need in lifetime management, since it's in one place.
However, when you use IoC container, your data repository are created outside your controller. Since IoC container that creates it for you doesn't know how and how long you're going to use the created object, a lifetime strategy is introduced.
For example, data context (repository) is usually created at the beginning of the web request and destroyed at the end. However, for components that work with external web service, or some static mapper (e.g. logger) there's no need to create them each time. So you may want to say to create them once (i.e. singletone lifestyle).
All this happen because IoC container (like Unity) are designed to handle many situations, and they don't know your specific needs. For example, some applications use "conversation" transactions where NHibernate (or Entity Framework maybe) may last during several pages / web requests. IoC containers allow you to tweak objects lifetime to suit your needs. But as said this comes at price - since there's no single predefined strategy, you have to select one yourself.
Why NerdDinner and other applications do not use more advanced techniques is simply because they are intended to demonstrate MVC features, not advanced usages of some other libraries. I remember an article written to demonstrate one IoC container advanced functionality - this article broke some approved design patterns like separation of concerns - but this wasn't that important because design patterns were not the goal of the article. Same with simple MVC-demonstration-applications - they do not want you, the MVC newcomer, to be lost in IoC labyrinths.
And I would not recommend to look at Oxite as a design reference example:
http://codebetter.com/blogs/karlseguin/archive/2008/12/15/oxite-oh-dear-lord-why.aspx
http://ayende.com/Blog/archive/2008/12/19/oxite-open-exchangable-informative-troubled-engine.aspx
我相信,大多数(如果不是全部)DI 容器都涉及生命周期的概念。根据所涉及的场景,您可能希望容器始终返回已注册组件的相同实例,而对于另一个组件,您可能希望它始终返回新实例。大多数容器还允许您指定在特定上下文中,您希望它返回相同的实例等。
我不太了解Unity(到目前为止我已经使用过Windsor和Autofac),但我怀疑Web请求生命周期管理器是生命周期策略的实现,其中容器在单个 Web 请求的生命周期内提供相同的实例。您会在像 Windsor 这样的容器中找到类似的策略。
最后,我想您指的是工作单元。工作单元本质上是一组您希望作为一个原子业务事务成功或失败的操作。有关更正式的描述,请查看 Martin Fowler 的定义。这是一个在领域驱动设计背景下越来越受欢迎的概念。工作单元会跟踪您在此类事务中应用的更改,并且在适当的时候,它会在一个 ACID 事务中提交这些更改。例如,在 NHibernate 中,会话支持工作单元的概念,更具体地说是更改跟踪,而在 Linq2SQL 中,它是上下文...
Most if not all of the DI containers touch the concept of life times, I believe. Depending on the scenario involved, you may want the container to always return the same instance of a registered component, while for another component, you may want it to always return a new instance. Most containers also allow you to specify that within a particular context, you want it to return the same instance, etc..
I don't know Unity very well (so far I have used Windsor and Autofac), but I suspect the web request lifetime manager to be an implementation of lifetime strategies where the same instance is provided by the container during the lifetime of a single web request. You will find similar strategies in containers like Windsor.
Finally, I suppose you are referring to Unit of Work. A Unit of Work is in essence a group of actions that you want to succeed or fail as one atomic business transaction. For a more formal description, look at Martin Fowler's definition. It is a concept that has gained more popularity in the context of Domain Driven Design. A unit of work keeps track of the changes you apply in such a transaction, and when the time is right, it commits these changes in one ACID transaction. In NHibernate e.g., the session supports the notion of unit of work and more specifically the change tracking, while in Linq2SQL it is the Context ...