好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ControllerDescriptor和ActionDescriptor的创建

ControllerDescriptor和ActionDescriptor的创建

不论是用于描述Controller的ControllerDescriptor,还是用于描述Action方法的ActionDescriptor,都具有同步和异步两个版本,那么这些不同类型的ControllerDescriptor的ActionDescriptor是在什么情况下创建的呢?

一、ControllerActionInvoker与AsyncControllerActionInvoker

ControllerDescriptor的创建设计到一个重要的名为ActionInvoker的组件,顾名思义,ActionInvoker专门用于Action方法的执行。我们会在本书第7章“Action方法的执行”中对ActionInvoker进行深入介绍,在这里我们只需要对其作一个简单的了解。

ActionInvoker实现了具有如下定义的 IActionInvoker 接口,唯一的方法实现了对指定Action方法的执行,而作为Controller的默认基类的 Controller 具有一个ActionInvoker属性,该属性表示的ActionInvoker被真正用于定义在该Controller类型中的所有Action方法的执行。

    1:   public   interface  IActionInvoker
    2:  {
    3:       bool  InvokeAction(ControllerContext controllerContext,  string  actionName);
    4:  }
    5:   
    6:   public   abstract   class  Controller
    7:  {   
    8:       //其它成员 
    9:       public  IActionInvoker ActionInvoker { get; set; }
   10:  }

而具有如下定义的System.Web.Mvc.Async.IAsyncActionInvoker接口是ActionInvoker的异步版本。IAsyncActionInvoker继承了IActionInvoker接口,并在此基础上定义了两个BeginInvokeAction/EndInvokeAction方法用于Action方法的异步执行。

    1:   public   interface  IAsyncActionInvoker : IActionInvoker
    2:  {
    3:      IAsyncResult BeginInvokeAction(ControllerContext controllerContext,   string  actionName, AsyncCallback callback,  object  state);
    4:       bool  EndInvokeAction(IAsyncResult asyncResult);
    5:  }

ASP.NET MVC真正用于Action方法同步和异步执行的ActionInvoker分别是 ControllerActionInvoker 和 AsyncControllerActionInvoker 。如下面的代码片断所示,ControllerActionInvoker定义了一个受保护的方法GetControllerDescriptor用于根据指定的Controller上下文获取相应的ControllerDescriptor,它的子类AsyncControllerActionInvoker对这个方法进行了重写。

    1:   public   class  ControllerActionInvoker : IActionInvoker
    2:  {
    3:       //其它成员 
    4:       protected   virtual  ControllerDescriptor GetControllerDescriptor( ControllerContext controllerContext);
    5:  }
    6:   
    7:   public   class  AsyncControllerActionInvoker : ControllerActionInvoker,  IAsyncActionInvoker, IActionInvoker
    8:  {
    9:       //其它成员 
   10:       protected   override  ControllerDescriptor GetControllerDescriptor( ControllerContext controllerContext);
   11:  }

我们所有要了解的是在默认情况下(没有对Controller类型的ActionInvoker属性进行显式设置)采用的ActionInvoker类型是哪个。ASP.NET MVC对Conroller采用的ActionInvoker类型的选择机制是这样的:

通过当前的DependencyResolver以IAsyncActionInvoker接口去获取注册的ActionInvoker,如果返回对象不为Null,则将其作为默认的ActionInvoker 。 通过当前的DependencyResolver以IActionInvoker接口去获取注册的ActionInvoker,如果返回对象不为Null,则将其作为默认的ActionInvoker 。 创建AsyncControllerActionInvoker对象作为默认的ActionInvoker 。

在默认的情况下,当前的DependencyResolver直接通过对指定的类型进行反射来提供对应的实例对象,所以对于前面两个步骤返回的对象均为Null,所以默认创建出来的ActionInvoker类型为AsyncControllerActionInvoker。我们可以通过如下一个简单的实例来验证这一点。在通过Visual Studio的ASP.NET MVC项目模板创建的空Web应用中,我们创建了如下一个默认的HomeController,在Action方法Index中直接通过ContentResult将ActionInvoker属性的类型名称呈现出来。

    1:   public   class  HomeController : Controller
    2:  {  
    3:       public  ActionResult Index()
    4:      {
    5:           return  Content( "默认ActionInvoker类型:"  +  this .ActionInvoker.GetType().FullName);
    6:      }
    7:  }   

当运行该Web应用时,会在浏览器上产生如下的输出结果,我们可以清楚地看到默认采用的ActionInvoker类型正是AsyncControllerActionInvoker。

    1:  默认ActionInvoker类型:System.Web.Mvc.Async.AsyncControllerActionInvoker

为了进一步验证基于DependencyResolver对ActionInvoker的提供机制,我们将在《 ASP.NET MVC Controller激活系统详解:IoC的应用[下篇] 》创建的基于Ninject的自定义NinjectDependencyResolver应用在这里。如下面的代码片断所示,在初始化NinjectDependencyResolver的时候,我们将IActionInvoker和IAsyncActionInvoker影射到两个自定义ActionInvoker类型,即FooActionInvoker和FooAsyncActionInvoker,它们分别继承自ControllerActionInvoker和AsyncControllerActionInvoker。

    1:   public   class  NinjectDependencyResolver : IDependencyResolver
    2:  {
    3:       public  IKernel Kernel { get;  private  set; }
    4:       public  NinjectDependencyResolver()
    5:      {
    6:           this .Kernel =  new  StandardKernel();
    7:          AddBindings();
    8:      }
    9:       private   void  AddBindings()
   10:      {
   11:           this .Kernel.Bind<IActionInvoker>().To<FooActionInvoker>();
   12:           this .Kernel.Bind<IAsyncActionInvoker>().To<FooAsyncActionInvoker>();
   13:      }
   14:       public   object  GetService(Type serviceType)
   15:      {
   16:           return   this .Kernel.TryGet(serviceType);
   17:      }
   18:       public  IEnumerable< object > GetServices(Type serviceType)
   19:      {
   20:           return   this .Kernel.GetAll(serviceType);
   21:      }
   22:  }
   23:   public   class  FooActionInvoker : ControllerActionInvoker
   24:  {}
   25:   public   class  FooAsyncActionInvoker : AsyncControllerActionInvoker
   26:  {}

在Global.asax中对NinjectDependencyResolver进行注册后运行我们的程序,会在浏览器中得到如下的输出结果。IAsyncActionInvoker和FooAsyncActionInvoker进行了影射,NinjectDependencyResolver可以通过IAsyncActionInvoker提供一个FooAsyncActionInvoker实例。

    1:  默认ActionInvoker类型:Artech.Mvc.FooAsyncActionInvoker

现在我们对NinjectDependencyResolver的定义稍加修改,将针对IAsyncActionInvoker接口的类型影射删除,只保留针对IActionInvoker的映射。

    1:   public   class  NinjectDependencyResolver : IDependencyResolver
    2:  {
    3:       //其它成员 
    4:       private   void  AddBindings()
    5:      {
    6:           this .Kernel.Bind<IActionInvoker>().To<FooActionInvoker>();
    7:           //this.Kernel.Bind<IAsyncActionInvoker>().To<FooAsyncActionInvoker>(); 
    8:      }
    9:  }

再次运行我们的程序则会得到如下的输出结果。由于NinjectDependencyResolver只能通过IActionInvoker接口提供具体的ActionInvoker,所以最终被创建的是一个FooActionInvoker对象。这个实例演示告诉我们:当我们需要使用到自定义的ActionInvoker的时候,可以通过自定义DependencyResolver以IoC的方式提供具体的ActionInvoker实例。

    1:  默认ActionInvoker类型:Artech.Mvc.FooActionInvoker


二、ActionInvoker与ControllerDescriptor/ActionDescriptor

ControllerDescriptor和ActionDescriptor最终是为Model绑定服务的,而Model绑定是Action执行的一个环节,所以ControllerDescriptor最终是由相应的ActionInvoker创建的。总的来说, 用于同步Action执行的ControllerActionInvoker创建ReflectedControllerDescriptor,而用于异步Action执行的AsyncControllerActionInvoker则创建ReflectedAsyncControllerDescriptor 。ActionInvoker和ControllerDescriptor之间的关系可以通过如下图所示的UML来表示。

对于ReflectedControllerDescriptor来说,包含其中的ActionDescriptor类型均为ReflectedActionDescriptor。而ReflectedAsyncControllerDescriptor描述的Controller可以同时包含同步和异步的Action方法,所以它可以包含任何类型的ActionDescriptor。

具体来说, 如果Action方法的返回类型为Task,对应的ActionDescriptor是一个TaskAsyncActionDescriptor对象 。 如果Action方法名称包含“Async”后缀,并且存在一个对应的以“Completed”后缀的方法,则对应的ActionDescriptor是一个ReflectedAsyncActionDescriptor对象 ;如果对应Completed方法不存在,对应的FindAction方法会直接抛出一个InvalidOperationException异常。在其它情况下的Action方法均是同步的,所以对应的ActionDescriptor类型为ReflectedActionDescriptor。ControllerDescriptor与ActionDescriptor之间的关系如下图所示的UML来表示。

三、实例演示:AsyncActionInvoker对ControllerDescriptor的创建

为了让读者对ActionInvoker对ControllerDescriptor的解析机制具有一个深刻的理解,同时也作为对该机制的验证,我们做一个简单的实例演示。通过前面的介绍我们知道在默认的情况下Controller采用AsyncControllerActionInvoker进行Action方法的执行,这个例子就来演示一下它生成的ControllerDescriptor是个怎样的对象。我们通过Visual Studio的ASP.NET MVC项目模板创建一个空Web应用,并创建一个默认的HomeController,然后对其进行如下的修改。

    1:   public   class  HomeController : AsyncController
    2:  {
    3:       public   void  Index()
    4:      {
    5:          MethodInfo method =  typeof (AsyncControllerActionInvoker).GetMethod( "GetControllerDescriptor" , BindingFlags.Instance | BindingFlags.NonPublic);
    6:          ControllerDescriptor controllerDescriptor = (ControllerDescriptor)method.Invoke( this .ActionInvoker,  new   object [] {  this .ControllerContext });
    7:          Response.Write(controllerDescriptor.GetType().FullName +  "<br/>" );
    8:          CheckAction(controllerDescriptor,  "Foo" );
    9:          CheckAction(controllerDescriptor,  "Bar" );
   10:          CheckAction(controllerDescriptor,  "Baz" );
   11:   
   12:      }
   13:       private   void  CheckAction(ControllerDescriptor controllerDescriptor,  string  actionName)
   14:      {
   15:          ActionDescriptor actionDescriptor = controllerDescriptor.FindAction( this .ControllerContext, actionName);
   16:          Response.Write( string .Format( "{0}:{1}<br/>" ,actionName, actionDescriptor.GetType().FullName));
   17:      }
   18:   
   19:       public   void  Foo() { }
   20:       public   void  BarAsync() { }
   21:       public   void  BarCompleted() { }
   22:       public  Task Baz()
   23:      {
   24:           return   new  Task(DoNothing);
   25:      }
   26:       public   void  DoNothing() { }
   27:  }

我们首先将HomeController的基类从Controller改为AsyncController,并定义了Foo、BarAsync/BarCompleted和Baz四个方法,我们知道它们对应着Foo、Bar和Baz三个Action,其中Foo是同步Action,Bar和Baz分别是两种不同定义形式(XxxAsync/XxxCompleted和Task)的异步Action。

CheckAction用于根据指定的Action名称从ControllerDescriptor对象中获取用于表示对应Action的ActionDescriptor对象,最终将类型名称呈现出来。在Index方法中,我们通过反射的方式调用当前ActionInvoker(一个AsyncControllerActionInvoker对象)的受保护方法GetControllerDescriptor或者用于描述当前Controller(HomeController)的ControllerDescriptor的对象,并将类型名称呈现出来。最后通过调用CheckAction方法将包含在创建的ControllerDescriptor对象的三个ActionDescriptor类型呈现出来。

当我们运行该程序的时候,在浏览器中会产生如下的输出结果,从中可以看出ControllerDescriptor类型为ReflectedAsyncControllerDescriptor。同步方法Foo对象的ActionDescriptor是一个ReflectedActionDescriptor对象;以XxxAsync/XxxCompleted形式定义的异步方法Bar对应的ActionDescriptor是一个ReflectedAsyncActionDescriptor对象;而返回类型为Task的方法Baz对应的ActionDescriptor类型则是TaskAsyncActionDescriptor。

    1:  System.Web.Mvc.Async.ReflectedAsyncControllerDescriptor
    2:  Foo:System.Web.Mvc.ReflectedActionDescriptor
    3:  Bar:System.Web.Mvc.Async.ReflectedAsyncActionDescriptor
    4:  Baz:System.Web.Mvc.Async.TaskAsyncActionDescriptor

 

分类:  [01] 技术剖析

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于ControllerDescriptor和ActionDescriptor的创建的详细内容...

  阅读:48次