也许你会问ASP.NET MVC为什么会爱上IoC?

  相爱的理由常常很简单,就像一首歌中所唱——“只为相遇那一个眼神”。

  而ASP.NET MVC爱上IoC只为IoC能实现MVC控制器的依赖注入。

  下面是博客园招聘频道(job.cnblogs.com)所用的一个MVC控制器:

public class EnterpriseController
{
protected IJobService _jobService;
protected IEnterpriseService _enterpriseService; #region Constructors
public EnterpriseController(IJobService jobService,
IEnterpriseService enterpriseService)
{
_jobService = jobService;
_enterpriseService = enterpriseService;
}
#endregion
}

  如上面的代码所示,有了IoC进行依赖注入,就不需要在构造函数中专门创建对应于_jobService与_enterpriseService的实例。IoC容器会在运行时自动创建IJobService与IEnterpriseService的实例,并传递给EnterpriseController的构造函数。

  就因为这一点,MVC就爱上了IoC。爱就这么简单。

  但是相爱容易,相处难。。。相处的过程中总会遇到各种各样的问题。。。所以幸福来自于你是否能努力解决这些问题。

  代码世界也一样,当我们让MVC与IoC相处时,就遇到了问题。这里我们以IoC容器Unity为例,说明一下我们遇到的问题与解决方法。

  要想实现Controller的依赖注入,就需要让IoC容器接管Controller的创建,而ASP.NET MVC 3中提供的IDependencyResolver接口就为实现这个提供了可能。所以,我们首先创建一个实现IDependencyResolver接口的UnityDependencyResolver类,代码如下:

public class UnityDependencyResolver : IDependencyResolver
{
IUnityContainer container; public UnityDependencyResolver(IUnityContainer container)
{
this.container = container;
} public object GetService(Type serviceType)
{
return container.Resolve(serviceType);
} public IEnumerable<object> GetServices(Type serviceType)
{
return container.ResolveAll(serviceType);
}
}

  UnityDependencyResolver的作用就是调用IoC容器(这里是Unity)解析相应类型的实例。创建了UnityDependencyResolver,我们还需要告诉MVC用它进行解析。在Global.asax的Application_Start()方法中添加如下代码:

protected void Application_Start()
{
IUnityContainer container = new UnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}

  我们运行一下程序试试,出现下面的错误提示:

The current type, System.Web.Mvc.IControllerFactory, is an interface and cannot be constructed. Are you missing a type mapping?

  从上面的错误信息可以分析出,错误是发生在调用UnityDependencyResolver.GetService方法时。ASP.NET MVC在运行的时候需要得到IControllerFactory的实现实例,然后用它去创建相应的控制器实例。如果不用IoC容器,MVC默认会创建DefaultControllerFactory的实例。现在用了IoC,MVC找不到IControllerFactory的实现实例(我们根本没有注册嘛),所以出现上面的错误。

  为了解决这个问题,我们注册一下DefaultControllerFactory:

container.RegisterType<IControllerFactory, DefaultControllerFactory>();

  继续运行程序,又出现新的错误:

The current type, System.Web.Mvc.IControllerActivator, is an interface and cannot be constructed. Are you missing a type mapping?

  找不到IControllerActivator的实现实例,看来,创建Controller还需要这个东东。查看MVC的源代码发现IControllerActivator的默认实现是DefaultControllerActivator,但郁闷的是它竟然是private class,无法注册它。别无选择,只能自己实现IControllerActivator,名叫CustomControllerActivator,代码如下:

public class CustomControllerActivator : IControllerActivator
{
IController IControllerActivator.Create(
System.Web.Routing.RequestContext requestContext,
Type controllerType)
{
return DependencyResolver.Current
.GetService(controllerType) as IController;
}
}

  继续运行,又出现新的错误:

The current type, System.Web.Mvc.IViewPageActivator, is an interface and cannot be constructed. Are you missing a type mapping?

  天哪!难道MVC中的所有接口都要注册一下。。。

  这时,脑子里突然闪出一个指示牌:

  于是,脚踩刹车,打了一把方向盘,驶上了另一条道 —— 如果IoC容器中没有注册,不引发异常,而是返回null,让MVC用自己的方式去处理。

  修改UnityDependencyResolver的GetService方法:

public object GetService(Type serviceType)
{
if (!this.container.IsRegistered(serviceType))
{
return null;
}
return container.Resolve(serviceType);
}

  并取消之前在IoC容器中对DefaultControllerFactory与CustomControllerActivator的注册。

  继续运行,成功!虽然成功,但停车一看,原来兜了一个圈子,又回到了出发的地方。一切还是交由MVC处理,IoC容器形同虚设,Controller的依赖注入无法实现。如果这时访问想依赖注入的Controller(构造函数带有参数),会出现下面的错误提示:

No parameterless constructor defined for this object.

  虽然回到原地,看上去没有前进一步,但实际上你已离目标更近一些(积累了经验,下次前进速度会更快)。就像你追一个女孩子,费尽心思,却被拒绝,看似你的一切努力付之流水,实际上她的心门已经有点松动。。。这时,你要有一种锲而不舍的精神,把失落感扔到九霄云外,然后继续努力,坚信“精诚所至,金石为开”。解决技术问题也是同样道理。

  重头再来!阅读MVC的源代码,了解MVC的请求处理过程,看看MVC是在什么地方创建Controller的实例的,然后看有没有办法让IoC容器来接管。

  MvcHandler.BeginProcessRequest->MvcHandler.ProcessRequestInit,呵呵,找到:  

factory = ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(RequestContext, controllerName);

  上面的代码中,factory的类型是IControllerFactory,ControllerBuilder.GetControllerFactory()的作用是获取IControllerFactory的实现实例,而实际是通过调用IDependencyResolver接口得到的(我们之前实现的UnityDependencyResolver接管了IDependencyResolver接口)。但我们没有在IoC容器中注册IControllerFactory,实际是由MVC返回IControllerFactory的默认实现DefaultControllerFactory。从上面的代码还可以看出,Controller实例的创建是通过调用IControllerFactory.CreateController()方法,所以,我们要在DefaultControllerFactory.CreateController()方法中寻找线索,对应代码如下:

public virtual IController CreateController(RequestContext requestContext, string controllerName) {
Type controllerType = GetControllerType(requestContext, controllerName);
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
}

  CreateController()又调用了GetControllerInstance()得到Controller的实例,进一步查看其代码:

protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
return ControllerActivator.Create(requestContext, controllerType);
}

  ControllerActivator的类型是IControllerActivator,之前也提到过,IControllerActivator的默认实现是DefaultControllerActivator,由此可以看出,Controller实例的创建是由DefaultControllerActivator完成的。我们要实现依赖注入,就要由IoC容器来接管。

  那如何来接管呢?——重载DefaultControllerFactory的CreateController方法,将创建Controller实例的工作转交给IoC容器,代码如下:

public class UnityControllerFactory : DefaultControllerFactory
{
IUnityContainer container;
public UnityControllerFactory(IUnityContainer container)
{
this.container = container;
} protected override IController GetControllerInstance(RequestContext reqContext,
Type controllerType)
{
return container.Resolve(controllerType) as IController;
}
}

  然后在IoC容器中注册一下UnityControllerFactory:

container.RegisterType<IControllerFactory, UnityControllerFactory>();

  然后,运行程序。。。功夫不负有心人,依赖注入成功,问题解决!

  小结

  要实现ASP.NET MVC控制器的依赖注入,我们需要:

  1. 实现IDependencyResolver接口并通过DependencyResolver.SetResolver告知MVC,将部分类型实例解析工作交由IoC容器来处理;

  2. 继承DefaultControllerFactory,重载GetControllerInstance方法,并通过IoC容器将之注册为IControllerFactory的实现。

ASP.NET MVC使用IoC的更多相关文章

  1. ASP.NET MVC 使用 IOC框架 AutoFac 自动释放数据库资源

    每次使用using或者dispose来释放资源会比较麻烦,作为一个会偷懒的程序员来说当然不能接受了. 一.引用 autofac.dll和autofac.integration.mvc.dll 二.打开 ...

  2. ASP.NET MVC IOC 之Ninject攻略

    ASP.NET MVC IOC 之Ninject攻略 一.为什么要使用Ninject? 很多其它类型的IOC容器过于依赖配置文件,老是配置,总感觉有点不爽,而且要使用assembly-qualifie ...

  3. ASP.NET MVC IOC之Unity攻略

    ASP.NET MVC IOC之Unity攻略 一.你知道IOC与DI吗? 1.IOC(Inversion of Control )——控制反转 即依赖对象不在被依赖模块的类中直接通过new来获取 先 ...

  4. ASP.NET MVC不可或缺的部分——DI(IOC)容器及控制器重构的剖析(DI的实现原理)

    IoC框架最本质的东西:反射或者EMIT来实例化对象.然后我们可以加上缓存,或者一些策略来控制对象的生命周期,比如是否是单例对象还是每次都生成一个新的对象. DI实现其实很简单,首先设计类来实现接口, ...

  5. ASP.NET MVC不可或缺的部分——DI(IOC)容器及控制器重构的剖析

    ASP.NET MVC不可或缺的部分——DI(IOC)容器及控制器重构的剖析   IoC框架最本质的东西:反射或者EMIT来实例化对象.然后我们可以加上缓存,或者一些策略来控制对象的生命周期,比如是否 ...

  6. Asp.net mvc 中Action 方法的执行(二)

    [toc] 前面介绍了 Action 执行过程中的几个基本的组件,这里介绍 Action 方法的参数绑定. 数据来源 为 Action 方法提供参数绑定的原始数据来源于当前的 Http 请求,可能包含 ...

  7. ASP.NET MVC不可或缺的部分——DI及其本质工作分析

    IoC框架最本质的东西:反射或者EMIT来实例化对象.然后我们可以加上缓存,或者一些策略来控制对象的生命周期,比如是否是单例对象还是每次都生成一个新的对象. DI实现其实很简单,首先设计类来实现接口, ...

  8. 基于ASP.NET MVC的ABP框架入门学习教程

    为什么使用ABP 我们近几年陆续开发了一些Web应用和桌面应用,需求或简单或复杂,实现或优雅或丑陋.一个基本的事实是:我们只是积累了一些经验或提高了对,NET的熟悉程度. 随着软件开发经验的不断增加, ...

  9. 深度解析 ASP.NET MVC 5

    ASP.NET MVC基础 IoC容器 ASP.NET MVC可扩展性 ASP.NET MVC Filters & Cache ASP.NET MVC AJAX ASP.NET MVC Cli ...

随机推荐

  1. (1)CorePlot配置

    先来看一下官方的说法,贴内容(https://code.google.com/p/core-plot/wiki/UsingCorePlotInApplications) Dependent Proje ...

  2. AIM Tech Round (Div. 1) D. Birthday 数学 暴力

    D. Birthday 题目连接: http://www.codeforces.com/contest/623/problem/D Description A MIPT student named M ...

  3. Mysql双主实战

    参考:http://dev.mysql.com/doc/refman/5.1/en/replication-options-slave.htmlhttp://blog.chinaunix.net/ui ...

  4. 如何获取php错误

    今天把项目放在测试服务器,但是出现一个问题,用的TP5框架,我把入口文件放在了根目录,访问的时候报错了,框架引导文件引入不了,也不报错,就是说访问不了. 所以就用了一段代码把错误获取出来了,代码如下: ...

  5. winform 中设置窗体的默认焦点

    转载:http://www.cnblogs.com/weekzero/p/3504513.html winform的窗体中,有时候需要设置默认焦点,有时候需要取消默认焦点. 设置默认焦点,最简单的方法 ...

  6. [Android自定义控件] Android Scroller工具类和GestureDetector的简单用法

    转载:http://ipjmc.iteye.com/blog/1615828 Android里Scroller类是为了实现View平滑滚动的一个Helper类.通常在自定义的View时使用,在View ...

  7. 完美解决“find: 路径必须在表达式之前:”

    使用find命令查找文件的时候会有如题的提示,只需略作修改即可.两种方法: ①可cd到其它目录再查找(当前目录下存在要查找的目标文件时会出现此类错误,换到其它目录下再执行相同的命令即可): ②如果目标 ...

  8. 对Emlog 6.0 Beta的完整代码审计过程

    Emlog 6.0 beta版本,这可能是最后一篇关于PHP语言CMS的代码审计文章,此次将详细记录完整的审计过程. 文章基本上完整记录小东的对此CMS审计过程,或许显得繁琐,但代码审计的过程就是这样 ...

  9. 如何打造一款可靠的WAF

    之前写了一篇<WAF防御能力评测及工具>,是站在安全运维人员选型WAF产品的角度来考虑的(优先从测试角度考虑是前职业病,毕竟当过3年游戏测试?!).本篇文章从WAF产品研发的角度来YY如何 ...

  10. linux实操常用命令总结

    1. linux目录操作命令 ------目录的增.删.改.查------ cd, pwd, ls, mkdir, mv, ls(du, df, tree) 切换目录,跟window的使用类似 cd ...