ASP.NET MVC进阶之路:深入理解Controller激活机制并使用Ioc容器创建对象

  本文标题说是"深入理解Controller"其实有点“标题党”的味道了。本篇只会探讨"Controller"的激活机制,也就是如何创建Controller的并调用的。本篇不是讲解Controller底层相关知识,不过后期博文会对其进行介绍。

0X1 DefaultControllerFactory

  DefaultControllerFactory对象可以说是Controller创建激活的一个重要类,在默认情况下(这里的默认情况指没有向MVC指定负责创建Controller的类)DefaultControllerFactory负责Controller的创建于激活。本文讨论的重点将会放在Controller的创建。

  我们先看DefaultControllerFactory类的几个方法

  public virtual IController CreateController(RequestContext requestContext, string controllerName);
        public virtual void ReleaseController(IController controller);
        protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType);
        protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType);
        protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName);

   我们可以看到CreateController方法,它主要的作用是“ 使用指定的请求上下文来创建指定的控制器”,也就是说其实是通过这个方法进行创建控制器的操作。我相信很多人看到这里的第一想法就是“虚方法重写不久完了吗“?别急,我们来看看CreateController的源代码:

public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
    if (requestContext == null)
    {
        throw new ArgumentNullException("requestContext");
    }
    if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())
    {
        throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
    }
    Type controllerType = this.GetControllerType(requestContext, controllerName);
    return this.GetControllerInstance(requestContext, controllerType);
}

CreateController源代码

  我们可以看到CreateController方法在对参数进行验证后会调用其实例本身的GetControllerInstance方法,由这个方法最后来创建Controller的实例。

0X2 第一种实现方式

  我们新建一个MVC应用,并新建一个名为NinjectController的类派生于DefaultControllerFactory,重写DefaultControllerFactory的GetControllerInstance方法,代码如下:

 1         private static IKernel _kernerl;
 2         public static IKernel Kernerl
 3         {
 4             get
 5             {
 6                 if (_kernerl == null)
 7                     _kernerl = new StandardKernel();
 8                 return _kernerl;
 9             }
10         }
11
12         static NinjectController()
13         {
14             //把Home绑定到Ninject里面
15             Kernerl.Bind<HomeController>().To<HomeController>();
16         }
17
18
19         protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
20         {
21             if (Kernerl == null)
22                 throw new NotImplementedException("Kinject未绑定任何数据");
23             if (controllerType == null || Kernerl.Get(controllerType) == null)
24                 return null;
25             return (IController)Kernerl.Get(controllerType);
26         }

第一种实现方法

  这种方法虽然可以实现,但是在开发中并不可取,我们再来看第二种方式。

0X3 第二种实现方式

  我们通过查看GetControllerInstance的源代码可以发现它最终调用了实例中ControllerActivator的Create方法进行创建Controller对象,ControllerActivator类型是一个IControllerActivator接口,那么这个ControllerActivator是在哪赋值的呢?我们看下DefaultControllerFactory的构造函数

public DefaultControllerFactory() : this(null, null, null)
{
}
public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null)
{
}

  DefaultControllerFactory有一个内部调用的方法,其源代码如下:

internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
{
    if (controllerActivator != null)
    {
        this._controllerActivator = controllerActivator;
        return;
    }
    IResolver<IControllerActivator> arg_44_1 = activatorResolver;
    if (activatorResolver == null)
    {
        arg_44_1 = new SingleServiceResolver<IControllerActivator>(() => null, new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory constructor");
    }
    this._activatorResolver = arg_44_1;
}

  通过阅读源码我们可以看到当IControllerActivator等于Null的时候,则会通过  DefaultControllerFactory.DefaultControllerActivator方法来创建IControllerActivator对象,我们继续阅读DefaultControllerActivator的源代码

 1 public IController Create(RequestContext requestContext, Type controllerType)
 2     {
 3         IController result;
 4         try
 5         {
 6             result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
 7         }
 8         catch (Exception innerException)
 9         {
10             throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[]
11             {
12                 controllerType
13             }), innerException);
14         }
15         return result;
16     }

  IControllerActivator的Create方法在创建IController对象的时候会从this._resoverThunk的GetServer方法中获取,this._resoverThunk是IControllerActivator的局部变量,该变量存储一个返回值为IDependencyResolver的委托,其定义如下:

private Func<IDependencyResolver> _resolverThunk;

  而对其赋值则来则与IControllerActivator的构造方法,其源代码如下:

public DefaultControllerActivator() : this(null)
    {
    }

    public DefaultControllerActivator(IDependencyResolver resolver)
    {
        if (resolver == null)
        {
            this._resolverThunk = (() => DependencyResolver.Current);
            return;
        }
        this._resolverThunk = (() => resolver);
    }

  从以上代码可以看出,this._resoverThunk实则是通过DependencyResolver.Current进行赋值的,而创建IController对象则是通过 this._resolverThunk().GetService(controllerType)来获取的,通俗来讲,默认的Conteroller实则是通过IDependencyResolver接口的GetService方法进行创建的,相信看到这里的朋友心中已经有了答案。没错,我们第二种做法就是通过自定义IDependencyResolver来创建我们的Contorller。

  相信很多朋友会问,IDependencyResolver接口在MVC中起到什么作用。IDependencyResolver定义如下:

public interface IDependencyResolver
    {

        object GetService(Type serviceType);
        IEnumerable<object> GetServices(Type serviceType);
    }

  一个获取单个类型的方法,一个获取多个对象的方法。有一个名为DependencyResolver的,它本身并不实现IDependencyResolver接口,所以它和IDependencyResolver没有任何关系。我们来看下DependencyResolver的定义。

  从上述代码我们可以清楚知道默认情况下实际上是调用了DependencyResolver的Current返回了当前对象,然后进行了对象的创建,那我们要做的只要改变Current的值就可以了,但是,我们有没有办法呢?答案是肯定的,DependencyResolver定义了一个SetResolver的方法,我们看下源码:

public void InnerSetResolver(object commonServiceLocator)
{
    if (commonServiceLocator == null)
    {
        throw new ArgumentNullException("commonServiceLocator");
    }
    Type type = commonServiceLocator.GetType();
    MethodInfo method = type.GetMethod("GetInstance", new Type[]
    {
        typeof(Type)
    });
    MethodInfo method2 = type.GetMethod("GetAllInstances", new Type[]
    {
        typeof(Type)
    });
    if (method == null || method.ReturnType != typeof(object) || method2 == null || method2.ReturnType != typeof(IEnumerable<object>))
    {
        throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DependencyResolver_DoesNotImplementICommonServiceLocator, new object[]
        {
            type.FullName
        }), "commonServiceLocator");
    }
    Func<Type, object> getService = (Func<Type, object>)Delegate.CreateDelegate(typeof(Func<Type, object>), commonServiceLocator, method);
    Func<Type, IEnumerable<object>> getServices = (Func<Type, IEnumerable<object>>)Delegate.CreateDelegate(typeof(Func<Type, IEnumerable<object>>), commonServiceLocator, method2);
    this.InnerSetResolver(new DependencyResolver.DelegateBasedDependencyResolver(getService, getServices));
}

  该方法最终会将指定的对象复制给Current,我们新建一个类名为NinjectDependencyResolver并实现IDependencyResolver接口。我们在类中定一个Ninject的字段,并封装一个用来往Ninject中添加对象的方法,其代码如下:

public class NinjectDependencyResolver : IDependencyResolver
    {
        private IKernel Kerner;

        NinjectDependencyResolver()
        {
            Kerner = new StandardKernel();
        }

        public void Registet<To, Form>() where Form : To
        {
            Kerner.Bind<To>().To<Form>();
        }
        public object GetService(Type serviceType)
        {
            return Kerner.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return Kerner.GetAll(serviceType);
        }
    }

  如需使用我们自定义的NinjectDependencyResolver,我们需要在Global.asax中对其进行注册:

protected void Application_Start()
        {
            //其他操作
            NinjectDependencyResolver ninjectDependencyResolver = new NinjectDependencyResolver();
            ninjectDependencyResolver.Registet<Controller, Controllers.HomeController>();//这里的Controller应该是具体类型,这里这样写只是为了演示
            DependencyResolver.SetResolver(ninjectDependencyResolver);
        }

  再次运行我们的程序,则会看到效果。

时间: 2024-10-01 00:18:09

ASP.NET MVC进阶之路:深入理解Controller激活机制并使用Ioc容器创建对象的相关文章

ASP.NET MVC进阶之路:深入理解依赖注入(DI)和控制反转(IOC)

0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点.在程序运行过程中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,然后将其注入到客户类中,保证客户类的正常运行.详细的介绍可以阅读上一篇博文:http://www.cnblogs.com/dazhuangtage/p/5672190.html 0X2 什么是控制反转 在解释什么是控

[ASP.NET MVC 小牛之路]01 - 理解MVC模式--转载

PS:MVC出来很久了,工作上一直没机会用.出于兴趣,工作之余我将展开对MVC的深入学习,通过博文来记录所学所得,并希望能得到各位园友的斧正. 本文目录 理解一般意义上的MVC模式 MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为以下三个基本部分: 模型(Model):模型用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法.“模型”有对数据直接访问的权力,例如对数据库的访问.“模型”不依赖“视图”和“控制器”,也就是说,模型不关心它会

ASP.NET MVC进阶之路:依赖注入(Di)和Ninject

0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点.在程序运行过程中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,然后将其注入到客户类中,保证客户类的正常运行. 图1 如图1所示,数据库操作类DataManager中依赖的IDataBase的接口,而不是以来IDataBase的具体实现类,这样的好处是可以让我们的程序具有扩展性:

[ASP.Net] 转 &gt; ASP.NET MVC 小牛之路

URL: http://www.cnblogs.com/willick/ 看到了不错的学习笔记,MVC.Net学习之路展开 [ASP.NET MVC 小牛之路]18 - Web API [ASP.NET MVC 小牛之路]17 - 捆绑(Bundle) [ASP.NET MVC 小牛之路]16 - Model 验证 [ASP.NET MVC 小牛之路]15 - Model Binding [ASP.NET MVC 小牛之路]14 - Unobtrusive Ajax [ASP.NET MVC 小

[ASP.NET MVC 大牛之路]03 - C#高级知识点概要(2) - 线程和并发

我也想过跳过C#高级知识点概要直接讲MVC,但经过前思后想,还是觉得有必要讲的.我希望通过自己的经验给大家一些指引,带着大家一起走上ASP.NET MVC大牛之路,少走弯路.同时也希望能和大家一起交流,这样也能发现我自己的不足,对我自己的帮助也是非常大的. 建议大家对C#撑握的不错的时候,可以去看一些开源项目.走技术这条路,就要耐得住寂寞(群里双休日说要让群主找妹子进群的人必须反思),练好内功.不撑握C#高级知识点,别想看懂优秀的开源项目,更别指望吸收其编程思想:你的水平,随时可以被一个实习生代

[ASP.NET MVC 小牛之路]18 - Web API

原文:[ASP.NET MVC 小牛之路]18 - Web API Web API 是ASP.NET平台新加的一个特性,它可以简单快速地创建Web服务为HTTP客户端提供API.Web API 使用的基础库是和一般的MVC框架一样的,但Web API并不是MVC框架的一部分,微软把Web API相关的类从 System.Web.Mvc 命名空间下提取了出来放在 System.Web.Http 命名空间下.这种理念是把 Web API 作为ASP.NET 平台的核心之一,以使Web API能使用在

[ASP.NET MVC 大牛之路]02 - C#高级知识点概要(1) - 委托和事件

在ASP.NET MVC 小牛之路系列中,前面用了一篇文章提了一下C#的一些知识点.照此,ASP.NET MVC 大牛之路系列也先给大家普及一下C#.NET中的高级知识点.每个知识点不太会过于详细,但足矣.要深入研究还需要去查看更多的专业资料. 要成为大牛,必然要有扎实的基本功,不然时间再长项目再多也很难有大的提升.本系列讲的C# 高级知识点,是非常值得去撑握的,不仅可以让你写代码时游刃有余,而且去研究和学习一些开源项目时,也不会显得那么吃力了. 希望大家记住,这里讲的所有的知识点,不仅仅是了解

MVC进阶之路:依赖注入(Di)和Ninject

MVC进阶之路:依赖注入(Di)和Ninject 0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点.在程序运行过程中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,然后将其注入到客户类中,保证客户类的正常运行. 图1 如图1所示,数据库操作类DataManager中依赖的IDataBase的接口,而不是以来IDataBase的具

[ASP.NET MVC 小牛之路]05 - 使用 Ninject实现依赖注入

在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来演示Ninject在ASP.NET MVC中的应用. 为了更好的理解和撑握本文内容,强烈建议初学者阅读本文前先阅读依赖注入(DI)和Ninject. 本文目录: 准备工作 新建一个名为BookShop的空白解决方案.在该解决方案中分别添加一个名为BookShop.WebUI的MVC空应用程序,和一个