189 8069 5689

想爱容易相处难:当ASP.NETMVC爱上IoC

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

创新互联-专业网站定制、快速模板网站建设、高性价比华池网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式华池网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖华池地区。费用合理售后完善,10多年实体公司更值得信赖。

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

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

下面是一个网站所用的一个MVC控制器:

 
 
 
 
  1. public class EnterpriseController  
  2. {  
  3.     protected IJobService _jobService;  
  4.     protected IEnterpriseService _enterpriseService;  
  5.  
  6.     #region Constructors  
  7.     public EnterpriseController(IJobService jobService,   
  8.         IEnterpriseService enterpriseService)  
  9.     {  
  10.         _jobService = jobService;  
  11.         _enterpriseService = enterpriseService;  
  12.     }  
  13.     #endregion  

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

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

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

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

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

 
 
 
 
  1. public class UnityDependencyResolver : IDependencyResolver  
  2. {  
  3.     IUnityContainer container;  
  4.  
  5.     public UnityDependencyResolver(IUnityContainer container)  
  6.     {  
  7.         this.container = container;  
  8.     }  
  9.  
  10.     public object GetService(Type serviceType)  
  11.     {  
  12.         return container.Resolve(serviceType);  
  13.     }  
  14.  
  15.     public IEnumerable GetServices(Type serviceType)  
  16.     {  
  17.         return container.ResolveAll(serviceType);  
  18.     }  
  19. UnityDependencyResolver的作用就是调用IoC容器(这里是Unity)解析相应类型的实例。创建了UnityDependencyResolver,我们还需要告诉MVC用它进行解析。在Global.asax的Application_Start()方法中添加如下代码:

     
     
     
     
    1. protected void Application_Start()  
    2. {  
    3.     IUnityContainer container = new UnityContainer();  
    4.     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:

     
     
     
     
    1. container.RegisterType(); 

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

    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,代码如下:

     
     
     
     
    1. public class CustomControllerActivator : IControllerActivator  
    2. {          
    3.     IController IControllerActivator.Create(  
    4.         System.Web.Routing.RequestContext requestContext,  
    5.         Type controllerType)  
    6.     {  
    7.         return DependencyResolver.Current  
    8.             .GetService(controllerType) as IController;  
    9.     }        

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

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

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

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

    [[40973]]

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

    修改UnityDependencyResolver的GetService方法:

     
     
     
     
    1. public object GetService(Type serviceType)  
    2. {  
    3.     if (!this.container.IsRegistered(serviceType))  
    4.     {  
    5.         return null;  
    6.     }  
    7.     return container.Resolve(serviceType);  

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

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

     
     
     
     
    1. No parameterless constructor defined for this object. 

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

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

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

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

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

     
     
     
     
    1. public virtual IController CreateController(RequestContext requestContext, string controllerName) {  
    2.     Type controllerType = GetControllerType(requestContext, controllerName);  
    3.     IController controller = GetControllerInstance(requestContext, controllerType);  
    4.     return controller;  

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

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

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

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

     
     
     
     
    1. public class UnityControllerFactory : DefaultControllerFactory  
    2. {  
    3.     IUnityContainer container;  
    4.     public UnityControllerFactory(IUnityContainer container)  
    5.     {  
    6.         this.container = container;  
    7.     }  
    8.  
    9.     protected override IController GetControllerInstance(RequestContext reqContext,  
    10.         Type controllerType)  
    11.     {  
    12.         return container.Resolve(controllerType) as IController;  
    13.     }  

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

     
     
     
     
    1. container.RegisterType(); 

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

    [[40974]]

    小结

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

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

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

    完整示例代码下载

    原文:http://www.cnblogs.com/dudu/archive/2011/08/15/mvc_ioc_dependency_injection.html


    当前名称:想爱容易相处难:当ASP.NETMVC爱上IoC
    文章位置:http://cdxtjz.cn/article/cogjphh.html

    联系我们

    您好HELLO!
    感谢您来到成都网站建设公司,若您有合作意向,请您为我们留言或使用以下方式联系我们, 我们将尽快给你回复,并为您提供真诚的设计服务,谢谢。
    • 电话:028- 86922220 18980695689
    • 商务合作邮箱:631063699@qq.com
    • 合作QQ: 532337155
    • 成都网站设计地址:成都市青羊区锣锅巷31号五金站写字楼6楼

    小谭建站工作室

    成都小谭网站建设公司拥有多年以上互联网从业经验的团队,始终保持务实的风格,以"帮助客户成功"为已任,专注于提供对客户有价值的服务。 我们已为众企业及上市公司提供专业的网站建设服务。我们不只是一家网站建设的网络公司;我们对营销、技术、管理都有自己独特见解,小谭建站采取“创意+综合+营销”一体化的方式为您提供更专业的服务!

    小谭观点

    相对传统的成都网站建设公司而言,小谭是互联网中的网站品牌策划,我们精于企业品牌与互联网相结合的整体战略服务。
    我们始终认为,网站必须注入企业基因,真正使网站成为企业vi的一部分,让整个网站品牌策划体系变的深入而持久。