ASP.NET MVC三个重要的描述对象: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分别是ControllerActionInvokerAsyncControllerActionInvoker。如下面的代码片断所示,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

 

ASP.NET MVC三个重要的描述对象:ControllerDescriptor

ASP.NET MVC三个重要的描述对象:ActionDescriptor

ASP.NET MVC三个重要的描述对象:ControllerDescriptor与ActionDescriptor的创建机制

ASP.NET MVC三个重要的描述对象:ParameterDescriptor

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-09-09 04:30:22

ASP.NET MVC三个重要的描述对象:ControllerDescriptor和ActionDescriptor的创建的相关文章

ASP.NET MVC三个重要的描述对象:ControllerDescriptor与ActionDescriptor

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

ASP.NET MVC三个重要的描述对象:ParameterDescriptor

Model绑定是为作为目标Action的方法准备参数列表的过程,所以针对参数的 描述才是Model绑定的核心.在ASP.NET MVC应用编程接口中,服务于Model绑定的 参数元数据通过ParameterDescriptor类型来表示,而ActionDescriptor的 GetParameters方法返回的就是一个ParameterDescriptor数组. 如下面的代码片断所示,ParameterDescriptor同样实现了 ICustomAttributeProvider接口提供应用在

ASP.NET MVC三个重要的描述对象:ControllerDescriptor

ASP.NET MVC应用的请求都是针对某个Controller的某个Action方法,所以对 请求的处理最终体现在对目标Action方法的执行.而Action方法具有相应的参数 ,所以在方法执行之前必须根据相应的规则从请求中提取相应的数据并将其转换 为Action方法参数列表,我们将这个过程称为Model绑定.在ASP.NET MVC应用编 程接口中,Action方法某个参数的元数据通过ParameterDescriptor表示,而两个 相关的类型ControllerDescriptor和Ac

ASP.NET MVC三个重要的描述对象:ActionDescriptor

在Model绑定过程中会通过激活的Controller类型创建用于描述它的 ControllerDescriptor对象.Controller是一组Action方法的集合,而每一个 Action通过ActionDescriptor对象来表示,在这篇文章中我们就来着重谈谈不同 类型的ActionDescriptor.[本文已经同步到<How ASP.NET MVC Works?>中] 一.ActionDescriptor 用于描述定义在Controller类中的Action方法的ActionDe

七天学会ASP.NET MVC (三)——ASP.Net MVC 数据处理

  第三天我们将学习Asp.Net中数据处理功能,了解数据访问层,EF,以及EF中常用的代码实现方式,创建数据访问层和数据入口,处理Post数据,以及数据验证等功能. 系列文章 七天学会ASP.NET MVC (一)--深入理解ASP.NET MVC 七天学会ASP.NET MVC (二)--ASP.NET MVC 数据传递 七天学会ASP.NET MVC (三)--ASP.Net MVC 数据处理 七天学会ASP.NET MVC (四)--用户授权认证问题 七天学会ASP.NET MVC (五

学习ASP.NET MVC(三) Controller/Action 深入解析与应用实例

一.摘要 一个Url请求经过了Routing处理后会调用Controller的Action方法. 中间的过程是怎样的? Action方 法中返回ActionResult对象后,如何到达View的? 本文将讲解Controller的基本用法, 深入分析 Controller的运行机制, 并且提供了创建所有类型Action的代码. 值得学习ASP.NET MVC时参考. 二.承上启下 在上一篇文章中, 我已经学会了如何使用Routing获取Controller和Action, 随后的程序会调用 Co

ASP.NET MVC的运行机制

一.ASP.NET + MVC IIS与ASP.NET管道 MVC.MVP以及Model2[上篇] MVC.MVP以及Model2[下篇] ASP.NET MVC是如何运行的[1]: 建立在"伪"MVC框架上的Web应用 ASP.NET MVC是如何运行的[2]: URL路由 ASP.NET MVC是如何运行的[3]: Controller的激活 ASP.NET MVC是如何运行的[4]: Action的执行 二.URL 路由 ASP.NET的路由系统:URL与物理文件的分离 ASP.

How ASP.NET MVC Works?

一.ASP.NET + MVC IIS与ASP.NET管道 MVC.MVP以及Model2[上篇] MVC.MVP以及Model2[下篇] ASP.NET MVC是如何运行的[1]: 建立在"伪"MVC框架上的Web应用 ASP.NET MVC是如何运行的[2]: URL路由 ASP.NET MVC是如何运行的[3]: Controller的激活 ASP.NET MVC是如何运行的[4]: Action的执行 二.URL 路由 ASP.NET的路由系统:URL与物理文件的分离 ASP.

[转自Scott]ASP.NET MVC框架(第一部分)

英文原文地址:http://weblogs.asp.net/scottgu/archive/2007/11/13/asp-net-mvc-framework-part-1.aspx 翻译原文地址:http://blog.joycode.com/scottgu/archive/2007/11/14/111385.aspx 两个星期前, 我在博客里讨论了ASP.NET的一个新MVC(模型.视图,控制器)框架,我们将在不久的将来作为一个可选功能来支持.该框架提供了一个结构化的模型,来加强应用中的清晰关