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分别是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
作者:Artech
出处:http://artech.cnblogs.com/
加支付宝好友偷能量挖...