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

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

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

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

public class EnterpriseController{protected IJobService _jobService;protected IEnterpriseService _enterpriseService;

#region Constructorspublic 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>();

  然后,运行程序。。。功夫不负有心人,依赖注入成功,问题解决!从此,MVC与IoC过上了幸福的生活。

  小结

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

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

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

  完整示例代码下载

想爱容易,相处难:当ASP.NET MVC爱上IoC相关推荐

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

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

  2. 我将在深圳MPD大会上分享“估算,想爱你好难!”

    我将会在深圳MPD大会上分享"估算,想爱你好难"! 本次大会收费,详情点击下文的"MPD日程安排". MPD简介: 亚太软件研发团队管理峰会 msup开放日(m ...

  3. 大学生都说学计算机专业难,大学单身率很高的4个专业,男女比例严重失调,想谈恋爱太难了...

    大学谈一场恋爱是不是想起来很让人心动,其实和爱的人在一起,你会发现每一天充满了乐趣.很多学生选择大学的时候谈恋爱,让自己的爱情变得更加完美. 大学的生活丰富多彩,同学们可能来自于五湖四海,相比于高中单 ...

  4. 让码农越爱越想爱的星座女TOP 5

    NO5.天蝎女  蝎族的人在心中总是有一个目标,非常有毅力,以不屈不挠的斗志和战斗力,深思熟虑的朝目标前进.这些在蝎女们看来,更多的会表现在事业上,能干,精炼.这样的女人绝对是伴侣事业上的好伙伴,因为 ...

  5. 致首次创业者:如果做到了这三点,想不成功都难(转)

    致首次创业者:如果做到了这三点,想不成功都难 2015-12-27 21:14 24人阅读 评论(0) 收藏 举报 分类: 互联网(2) 创业(4) 编者按:Rob Hayes 是 First Rou ...

  6. stm32f407能跑linux吗_跑步能跑进医院?那我该做跑步运动吗?想健康一点太难了...

    跑步能跑进医院?那我该做跑步运动吗?想健康一点太难了,小编学生时期最喜欢的课程永远都是体育课,但最头疼的项目却是磨人的800米和1000米,那简直是我的的噩梦,近日,西安一名初中生跑步居然跑进了医院, ...

  7. 这次国足想不出线都难

    关岛,马尔代夫,菲律宾,这次国足想不出线都难! 昨晚国足可圈可点: 1.拼劲十足,全场压制叙利亚 2.后防线稳固,只丢了一个球 3.武磊状态好,一人独造8个进球,可以说居功至伟 12强赛,国足来了!

  8. 计算机音乐好想爱这个世界,好想爱这个世界啊歌词

    好想爱这个世界啊LRC歌词 [00:00.000] 作曲 : 华晨宇 [00:00.391] 作词 : 裴育 [00:01.173]编曲:郑楠 [00:06.032]制作人:郑楠 [00:10.316 ...

  9. 想爱,爱不了,想恨,恨不下,想忘,忘不了

    总有那么个人,.想爱,爱不了 总有那么个人,想恨,恨不下 总有那么个人,想留,却留不住 总有那么个人,想放,却放不下 有的.有的.有的... 有的人,爱过了,就分开了 有的话,说过了,就后悔了 有道伤 ...

最新文章

  1. 【Markdown】如何在微信公众号上写markdown的文章
  2. Linux下静态编译的一个TIP
  3. 使用Linq读取资源文件
  4. 【问题解决】M5神库M5Stack-SD-Updater安装及使用方法
  5. opencv 显示图片
  6. 算法代码中的循环矩阵在哪体现_「Machine Learning 学习小结」| 向量在梯度下降算法当中的应用...
  7. 【uC/OS-II】笔记1----入门
  8. java安装路径_java环境变量和查看安装路径
  9. java的语法基础_JAVA语法基础1(入门手册)
  10. Centos7 网络报错Job for iptables.service failed because the control process exited with error code....
  11. 第二章 springboot+mybatis
  12. Atitit 未来数据库新特性展望目录1. 统一的翻页 21.1. 2 Easy Top-N
  13. 报错 应用程序池 中asp.net 4.0 自动停止
  14. 几道JAVA和分布式系统面试题总结
  15. Excel函数实战技巧精粹(二)常用函数之VLOOKUP全解
  16. [mooc]open course on github
  17. 微型计算机常用的分区格式,教你分出整数的硬盘分区
  18. win7下计算机假死,Win7系统电脑经常发生假死现象的五种情况及解决方法
  19. 单片机程序配置成开漏输出_单片机的GPIO配置
  20. python随机生成英文字符串_Python练习第六题,生成随机字母

热门文章

  1. css03层次选择器
  2. 新成立的Scala中心将重点关注教育和Scala社区
  3. 云计算里AWS和Azure的探究(2)
  4. 纽约时报:人们正成为移动通讯的奴隶
  5. C语言 读取文件内容
  6. windows下配置apache虚拟主机
  7. ContrainedBox:设置尺寸
  8. 顺序表循环队列:创建初始化、入队、出队、获取队列头数据、计算队列有效数据长度...
  9. 【Codeforces Round #430 (Div. 2) D】Vitya and Strange Lesson
  10. 嵌入式第一周学习总结