本文标题说是"深入理解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方法,代码如下:

         private static IKernel _kernerl;
public static IKernel Kernerl
{
get
{
if (_kernerl == null)
_kernerl = new StandardKernel();
return _kernerl;
}
} static NinjectController()
{
//把Home绑定到Ninject里面
Kernerl.Bind<HomeController>().To<HomeController>();
} protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (Kernerl == null)
throw new NotImplementedException("Kinject未绑定任何数据");
if (controllerType == null || Kernerl.Get(controllerType) == null)
return null;
return (IController)Kernerl.Get(controllerType);
}

第一种实现方法

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

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的源代码

 public IController Create(RequestContext requestContext, Type controllerType)
{
IController result;
try
{
result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
}
catch (Exception innerException)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[]
{
controllerType
}), innerException);
}
return result;
}

  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);
}

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

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

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

    0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点.在程序运行过程中,客户 ...

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

    本人博客已转移至:http://www.exblr.com/liam  PS:MVC出来很久了,工作上一直没机会用.出于兴趣,工作之余我将展开对MVC的深入学习,通过博文来记录所学所得,并希望能得到各 ...

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

    0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点.在程序运行过程中,客户 ...

  4. [ASP.Net] 转 > ASP.NET MVC 小牛之路

    URL: http://www.cnblogs.com/willick/ 看到了不错的学习笔记,MVC.Net学习之路展开   [ASP.NET MVC 小牛之路]18 - Web API [ASP. ...

  5. [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject

    本人博客已转移至:http://www.exblr.com/liam  为什么需要依赖注入 在[ASP.NET MVC 小牛之路]系列的理解MVC模式文章中,我们提到MVC的一个重要特征是关注点分离( ...

  6. [ASP.NET MVC 小牛之路]05 - 使用 Ninject

    在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来 ...

  7. [ASP.NET MVC 小牛之路]06 - 使用 Entity Framework

    在家闲着也是闲着,继续写我的[ASP.NET MVC 小牛之路]系列吧.在该系列的上一篇博文中,在显示书本信息列表的时候,我们是在程序代码中手工造的数据.本文将演示如何在ASP.NET MVC中使用E ...

  8. [ASP.NET MVC 小牛之路]10 - Controller 和 Action (2)

    继上一篇文章之后,本文将介绍 Controller 和 Action 的一些较高级特性,包括 Controller Factory.Action Invoker 和异步 Controller 等内容. ...

  9. [ASP.NET MVC 小牛之路]13 - Helper Method

    我们平时编程写一些辅助类的时候习惯用“XxxHelper”来命名.同样,在 MVC 中用于生成 Html 元素的辅助类是 System.Web.Mvc 命名空间下的 HtmlHelper,习惯上我们把 ...

随机推荐

  1. The introduction to Web.config of ASP.NET #Reprinted#

    花了点时间整理了一下ASP.NET Web.config配置文件的基本使用方法.很适合新手参看,由于Web.config在使用很灵活,可以自定义一些节点.所以这里只介绍一些比较常用的节点. <? ...

  2. cnzz广告管家使用心得

    步着谷歌广告管家的后尘,国内百度公司和知名网络流量数据统计公司cnzz也分别推出了百度广告管家和cnzz广告管家.笔者分别注册了这两个网站.通过一段时间的使用,写下此文,希望对各位站长朋友们有所帮助. ...

  3. 转: sublime text常用插件和快捷键

    Sublime Text 2是一个轻量.简洁.高效.跨平台的编辑器.博主之前一直用notepdd++写前端代码,用得也挺顺手了,早就听说sublime的大名,一直也懒得去试试看,认为都是工具用着顺手就 ...

  4. php 登录实例演示

    <pre name="code" class="python">一.模板的使用 (重点) a.规则 模板文件夹下[TPL]/[分组文件夹/][模板主 ...

  5. BZOJ 1005 明明的烦恼 (组合数学)

    题解:n为树的节点数,d[ ]为各节点的度数,m为无限制度数的节点数. 则               所以要求在n-2大小的数组中插入tot各序号,共有种插法: 在tot各序号排列中,插第一个节点的 ...

  6. 【转】 IE6 IE7 IE8 css bug兼容性解决方法总结归纳

    1:li边距“无故”增加 任何事情都是有原因的,li边距也不例外. 先描述一下具体状况:有些时候li边距会突然增 加很多,值也不固定(只在IE6/IE7有这种现象),让人摸不着头脑,仔细“研究”发现是 ...

  7. 增强Delphi.RemObject.DataAbstract的脚本功能:多数据库同时操作

    我们知道,通过Schema,一个DataAbstracService对应一个数据库:一个服务器可以包含多个DataAbstracService,从而实现对多个数据库的操作.通过事件处理我们可以在一个D ...

  8. I can do it!(贪心)

    I can do it! Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Tot ...

  9. linux 中多线程使用

    /* * 对于线程之间的操作: * 一个进程中会有多个线程,各个线程之间的或向切换会付出很小的代价相比于进程之间的切换 * 为什么要引入多线程: * 1.与进程相比,他是一种非常节俭的多任务的操作方式 ...

  10. Python 参数传递

    python中的变量: 一个变量是局部还是全局,在编译函数的时候就已经决定,因此读变量值的时候也不会逐层向外查找.变量是全局还是局域,根据如下3条: 1. 如果函数内部有global语句,那么它声明的 ...