ASP.NET MVC Controller激活系统详解:总体设计

我们将整个ASP.NET MVC框架划分为若干个子系统,那么针对请求上下文激活目标Controller对象的子系统被我们成为Controller激活系统。在正式讨论 Controller对象具体是如何被创建爱之前,我们先来看看Controller激活系统在ASP.NET MVC中的总体设计,了解一下组成该子系统的一些基本的组件,以及它们对应的接口或者抽象类是什么。

目录
一、Controller
二、 ControllerFactory
三、ControllerBuilder
实例演示:如何提升命名空间的优先级
针对Area的路由对象的命名空间
四、 Controller的激活与URL路由

一、Controller

我们知道作为Controller的类型直接或者间接实现了IController接 口。如下面的代码片断所示,IController接口仅仅包含一个参数类型为RequestContext的Execute方法。当一个 Controller对象被激活之后,核心的操作就是根据请求上下文解析出目标Action方法,并通过Model绑定机制从请求上下文中提取相应的数据 映射为方法的参数并最终执行Action方法。所有的这些操作都是调用这个Execute方法来执行的。

   1: public interface IController
   2: {
   3:     void Execute(RequestContext requestContext);
   4: }

定义在IController接口中的Execute是以同步的方式执行的。为了支持以异步方式对请求的处理,IController接口的异步版 本System.Web.Mvc.IAsyncController被定义出来。如下面的代码片断所示,实现了IAsyncController接口的异 步Controller的执行通过BeginExecute/EndExecute方法组合来完成。

   1: public interface IAsyncController : IController
   2: {
   3:     IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state);
   4:     void EndExecute(IAsyncResult asyncResult);
   5: }

抽象类ControllerBase实现了IController接口,它具有如下几个重要的属性。TemplateData、ViewBag和 ViewData用于存储从Controller向View传递的数据或者变量。其中TemplateData和ViewData具有基于字典的数据结 构,Key和Value分别表示变量的名称和值,所不同的前者用于存储基于当前HTTP上下文的变量(在完成当前请求后,存储的数据会被回收)。 ViewBag和ViewData具有相同的作用,甚至对应着相同的数据存储,它们之间的不同之处在于前者是一个动态对象,我们可以为其指定任意属性。

   1: public abstract class ControllerBase : IController
   2: {
   3:     //其他成员
   4:     public ControllerContext ControllerContext { get; set; }
   5:     public TempDataDictionary TempData { get; set; }
   6:     public object ViewBag { [return: Dynamic] get; }
   7:     public ViewDataDictionary ViewData { get; set; }
   8: }

ASP.NET MVC中我们会陆续遇到一系列的上下文(Context)对象,之前我们已经对表示请求上下文的RequestContext(HttpContext + RouteData)进行了详细的介绍,现在我们来介绍另一个具有如下定义的上下文类型ControllerContext

   1: public class ControllerContext
   2: {
   3:     //其他成员
   4:     public ControllerContext();
   5:     public ControllerContext(RequestContext requestContext, ControllerBase controller);
   6:     public ControllerContext(HttpContextBase httpContext,
   7:     RouteData routeData, ControllerBase controller);
   8:
   9:     public virtual ControllerBase Controller { get; set; }
  10:     public RequestContext RequestContext { get; set; }
  11:     public virtual HttpContextBase HttpContext { get; set; }
  12:     public virtual RouteData RouteData { get; set; }
  13: }

顾名思义,ControllerContext就是基于某个Controller对象的上下文。从如下的代码所 示,ControllerContext是实际上是对一个Controller对象和RequestContext的封装,这两个对象分别对应着定义在 ControllerContext中的同名属性,并且可以在构造函数中被初始化。而通过属性HttpContext和RouteData属性返回的 HttpContextBase和RouteData对象在默认情况下实际上就是组成RequestContext的核心元素。 ControllerContext的这四个属性都是可读可写的,我们对其进行任意地修改。当ControllerBase的Execute方法被执行的 时候,它会根据传入的ReuqestContext创建ControllerContext对象,而后续的操作可以看成是在该上下文中进行。

当我们在进行开发的时候,通过VS默认创建的Controller类型实际上继承自抽象类Controller。 该类型中定义了很多的辅助方法和属性以编程变得简单。如下面的代码片断所示,除了直接继承ControllerBase之外,Controller类型还 显式实现了IController和IAsyncController接口,以及代表ASP.NET MVC 四大筛选器(AuthorizationFilter、ActionFilter、ResultFilter和ExceptionFilter)的4个接 口。

   1: public abstract class Controller :
   2:     ControllerBase,
   3:     IController,
   4:     IAsyncController,
   5:     IActionFilter,
   6:     IAuthorizationFilter,
   7:     IExceptionFilter,
   8:     IResultFilter,
   9:     IDisposable,
  10:     ...
  11: {
  12:    //省略成员
  13: }

 

二、 ControllerFactory

ASP.NET MVC为Controller的激活定义相应的相应的工厂,我们将其统称为ControllerFactory,所有的ControllerFactory实现了接口IControllerFactory接 口。如下面的代码片断所示,Controller对象的激活最终最终通过IControllerFactory的CreateController方法来 完成,该方法的两个参数分别表示当前请求上下文和从路由信息中获取的Controller的名称(最初来源于请求地址)。

   1: public interface IControllerFactory
   2: {
   3:     IController CreateController(RequestContext requestContext, string controllerName);
   4:     SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
   5:     void ReleaseController(IController controller);
   6: }
   7: public enum SessionStateBehavior
   8: {
   9:     Default,
  10:     Required,
  11:     ReadOnly,
  12:     Disabled
  13: }

处理负责创建Controller处理请求之前,ControllerFactory还需要在完成请求处理之后实施对Controller的释放回 收,后者实现在ReleaseController方法中。IControllerFactory的另一个方法 GetControllerSessionBehavior方法返回一个SessionStateBehavior枚举。熟悉ASP.NET的读者应该对SessionStateBehavior不会感到陌生,它用于表示请求处理过程中会话状态支持的模式,它的四个枚举值分别具有如下的含义:

  • Default:使用默认 ASP.NET 逻辑来确定请求的会话状态行为。
  • Required:为请求启用完全的读写会话状态行为。
  • ReadOnly:为请求启用只读会话状态。
  • Disabled:禁用会话状态。

对于Default选项来说,ASP.NET通过映射的HttpHandler类型是否实现了相关接口来决定具体的会话状态控制行为。在 System.Web.SessionState命名空间下定义了IRequiresSessionState和 IRequiresSessionState接口,如下面的代码片断所示,这两个都是不具有任何成员的空接口(我们一般称之为标记接口),而 IReadOnlySessionState继承自IRequiresSessionState。如果HttpHandler实现了接口 IReadOnlySessionState,则意味着采用ReadOnly模式,如果只实现了IRequiresSessionState则采用 Required模式。

   1: public interface IRequiresSessionState
   2: {}
   3: public interface IReadOnlySessionState : IRequiresSessionState
   4: {}

具体采用何种会话状态行为取决于当前HTTP上下文(HttpContext.Current)。对于之前的版本,我们不能对当前HTTP上下文的 会话状态行为模式进行动态的修改,ASP.NET 4.0为HttpContext定义了如下一个SetSessionStateBehavior方法是我们可以自由地选择会话状态行为模式。相同的方法同 样定义在HttpContextBase中,它的子类HttpContextWrapper重写了这个方法并在内部会调用封装的HttpContext的 同名方法。

   1: public sealed class HttpContext : IServiceProvider, IPrincipalContainer
   2: {
   3:     //其他成员
   4: public void SetSessionStateBehavior(
   5:     SessionStateBehavior sessionStateBehavior);
   6: }
   7: public class HttpContextBase: IServiceProvider
   8: {
   9:     //其他成员
  10:     public void SetSessionStateBehavior(SessionStateBehavior sessionStateBehavior);
  11: }

 

三、ControllerBuilder

用于激活Controller对象的ControllerFactory最终通过ControllerBuilder注 册到ASP.NET MVC应用中。如下面的代码所示,ControllerBuilder定义了一个静态只读属性Current返回当前ControllerBuilder 对象,这是针对整个Web应用的全局对象。两个SetControllerFactory方法重载用于注册ControllerFactory的类型或者 实例,而GetControllerFactory方法返回一个具体的ControllerFactory对象。

   1: public class ControllerBuilder
   2: {
   3:     public IControllerFactory GetControllerFactory();
   4:     public void SetControllerFactory(Type controllerFactoryType);
   5:     public void SetControllerFactory(IControllerFactory controllerFactory);
   6:
   7:     public HashSet<string> DefaultNamespaces { get; }
   8:     public static ControllerBuilder Current { get; }
   9: }

具体来说,如果我们是注册的ControllerFactory的类型,那么GetControllerFactory在执行的时候会通过对注册类 型的反射(调用Activator的静态方法CreateInstance)来创建具体的ControllerFactory(系统不会对创建的 Controller进行缓存);如果注册的是一个具体的ControllerFactory对象,该对象直接从 GetControllerFactory返回。

被ASP.NET路由系统进行拦截处理后会生成一个用于封装路由信息的RouteData对象,而目标Controller的名称就包含在通过该 RouteData的Values属性表示的RouteValueDisctionary对象中,对应的Key为“controller”。而在默认的情 况下,这个作为路由数据的名称只能帮助我们解析出Controller的类型名称,如果我们在不同的命名空间下定义了多个同名的Controller类, 会导致激活系统无法确定具体的Controller的类型从而抛出异常。

为了解决这个问题,我们必须为定义了同名Controller类型的命名空间设置不同的优先级,具体来说我们有两种提升命名空间优先级的方式。第一种方式就是在调用RouteCollection的扩展方法MapRoute时指定一个命名空间的列表。通过这种方式指定的命名空间列表会保存在Route对象的DataTokens属性表示的RouteValueDictionary字典中,对应的Key为“Namespaces”。

   1: public static class RouteCollectionExtensions
   2: {
   3:     //其他成员
   4:     public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces);
   5:     public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces);
   6:     public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces);
   7: }

而另一种提升命名空间优先级的方式就是将其添加到当前的ControllerBuilder中的默认命名空间列表中。从上面的给出的 ControllerBuilder的定义可以看出,它具有一个HashSet<string>类型的只读属性 DefaultNamespaces就代表了这么一个默认命名空间列表。对于这两种不同的命名空间优先级提升方式,前者(通过路由注册)指定命名空间具有 更高的优先级。

实例演示:如何提升命名空间的优先级

为了让读者对此如何提升命名空间优先级具有一个深刻的印象,我们来进行一个简单的实例演示。我们使用Visual Studio提供的项目模板创建一个空的ASP.NET MVC应用,并且使用如下所示的默认路由注册代码。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     public static void RegisterRoutes(RouteCollection routes)
   4:     {
   5:         routes.MapRoute(
   6:             name: "Default",
   7:             url: "{controller}/{action}/{id}",
   8:             defaults: new { controller = "Home", action = "Index",
   9:                 id = UrlParameter.Optional }
  10:         );
  11:     }
  12:     protected void Application_Start()
  13:     {
  14:         //其他操作
  15:         RegisterRoutes(RouteTable.Routes);
  16:     }
  17: }
  18: public class MvcApplication : System.Web.HttpApplication
  19: {
  20:     public static void RegisterRoutes(RouteCollection routes)
  21:     {
  22:         routes.MapRoute(
  23:             name: "Default",
  24:             url: "{controller}/{action}/{id}",
  25:             defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
  26:         );
  27:     }
  28:     protected void Application_Start()
  29:     {
  30:         //其他操作
  31:         RegisterRoutes(RouteTable.Routes);
  32:     }
  33: }

然后我们在Controllers目录下添加一个.cs 文件,并在该文件中定义两个同名的Controller类。如下面的代码片断所示,这两个HomeCotroller类分别定义在命名空间Artech.MvcAppArtech.MvcApp.Controllers之中,而Index操作返回的是一个将Controller类型全名为内容的ContentResult对象。

   1: namespace Artech.MvcApp.Controllers
   2: {
   3:     public class HomeController : Controller
   4:     {
   5:         public ActionResult Index()
   6:         {
   7:             return this.Content(this.GetType().FullName);
   8:         }
   9:     }
  10: }
  11: namespace Artech.MvcApp
  12: {
  13:     public class HomeController : Controller
  14:     {
  15:         public ActionResult Index()
  16:         {
  17:             return this.Content(this.GetType().FullName);
  18:         }
  19:     }
  20: }

现在我们直接运行该Web应用。由于具有多个Controller与注册的路由规则相匹配导致ASP.NET MVC的Controller激活系统无法确定目标哪个类型的Controller应该被选用,所以会出现如下图所示的错误。[源代码从这里下载]

image

目前定义了HomeController的两个命名空间具有相同的优先级,现在我们将其中一个定义在当前ControllerBuilder的默认 命名空间列表中以提升匹配优先级。如下面的代码片断所示,在Global.asax 的Application_Start方法中,我们将命名空间“Artech.MvcApp.Controllers”添加到当前ControllerBuilder的DefaultNamespaces属性所示的命名空间列表中

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     protected void Application_Start()
   4:     {
   5:         //其他操作
   6:         ControllerBuilder.Current.DefaultNamespaces.Add("Artech.MvcApp.Controllers");
   7:     }
   8: }

对用同时匹配注册的路由规则的两个HomeController,由于“Artech.MvcApp.Controllers”命名空间具有更高的匹配优先级,所有定义其中的HomeController会被选用,这可以通过如下图所示的运行结果看出来。[源代码从这里下载]

image

为了检验在路由注册时指定的命名空间和作为当前ControllerBuilder的命名空间哪个具有更高匹配优先级,我们修改定义在Global.asax中的路由注册代码。如下面的代码片断所示,我们在调用RouteTable的静态属性Routes的MapRoute方法进行路由注册的时候指定了命名空间(“Artech.MvcApp”)

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     public static void RegisterRoutes(RouteCollection routes)
   4:     {
   5:         routes.MapRoute(
   6:             name: "Default",
   7:             url: "{controller}/{action}/{id}",
   8:             defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
   9:             namespaces:new string[]{"Artech.MvcApp"}
  10:         );
  11:     }
  12:
  13:     protected void Application_Start()
  14:     {
  15:         //其他操作
  16:         RegisterRoutes(RouteTable.Routes);
  17:         ControllerBuilder.Current.DefaultNamespaces.Add("Artech.MvcApp.Controllers");
  18:     }
  19: }

再次运行我们的程序会在浏览器中得到如图3-3所示的结果,从中可以看出定义在命名空间“Artech.MvcApp”中的 HomeController被最终选用,可见较之作为当前ControllerBuilder的默认命名空间,在路由注册过程中执行的命名空间具有更高 的匹配优先级,前者可以视为后者的一种后备。[源代码从这里下载]

image

在路由注册时指定的命名空间比当前ControllerBuilder的默认命名空间具有更高的匹配优先级,但是对于这两个集合中的所有命名空间却具有相同的匹配优先级。换句话说,用 于辅助解析Controller类新的命名空间分为三个梯队,简称为路由命名空间、ConrollerBuilder命名空间和Controller类型 命名空间,如果前一个梯队不能正确解析出目标Controller的类型,则将后一个梯队的命名空间作为后备;反之,如果根据某个梯队的命名空间进行解析 得到多个匹配的Controller类型,会直接抛出异常

现在我们对本例的路由注册代码作了如下的修改,为注册的路由对象指定了两个命名空间(分别是两个HomeContrller所在的命名空间),运行我们的程序依然会得到如第一张图所示的错误。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     public static void RegisterRoutes(RouteCollection routes)
   4:     {
   5:         routes.MapRoute(
   6:             name: "Default",
   7:             url: "{controller}/{action}/{id}",
   8:             defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
   9:             namespaces: new string[] { "Artech.MvcApp", "Artech.MvcApp.Controllers" }
  10:         );
  11:     }
  12:
  13:     protected void Application_Start()
  14:     {
  15:         //其他操作
  16:         RegisterRoutes(RouteTable.Routes);
  17:     }
  18: }

针对Area的路由对象的命名空间

针对某个Area的路由映射是通过相应的AreaRegistration进行注册的,具体来说是在AreaRegistration的RegisterArea方法中调用AreaRegistrationContext对象的MapRoute方法进行注册的。如果在调用MapRoute方法中指定了表示命名空间的字符串,将自动作为注册的路由对象的命名空间,否则会将表示AreaRegistration所在命名空间的字符串加上“.*”后缀作为路由对象的命名空间。这里所说的“路由对象的命名空间”指的就是通过Route对象的DataTokens属性表示的RouteValueDictionary对象中Key为“Namespaces”的字符串数组,而该字符串最终会转移到生成的RouteData的DataTokens中。

除此之外,在调用AreaRegistrationContext的MapRoute方法时还会在注册Route对象DataTokens中添加一个Key为“UseNamespaceFallback” 的条目表示是否采用后备命名空间对Controller类型进行解析。如果注册对象具有命名空间(调用MapRoute方法时指定了命名空间或者对应的 AreaRegistration类型定义在某个命名空间中),该条目的值为False;否则为True。该条目同样反映在通过该Route对象生成的 RouteData对象的DataTokens属性中。[关于ASP.NET MVC路由,在我的文章《ASP.NET MVC路由扩展:路由映射》中具有详细的介绍]

在解析Controller真实类型的过程中,会先通过RouteData包含的命名空间来解析Controller类型。如 果Controller类型解析失败,则通过包含在通过RouteData的DataTokens属性表示的RouteValueDictionary对 象中的这个UseNamespaceFallback值来判断是否使用“后备”命名空间进行解析。具体来说,如果该值为True或者不存在,则先通过当前 ControllerBuilder的命名空间解析,如果失败则忽略命名空间直接采用类型名称进行匹配;否则直接因找不到匹配的Controller而抛 出异常

我们通过具体的例子来说明这个问题。在一个通过Visual Studio的ASP.NET MVC项目创建的空Web应用中,我们添加一个名称为Admin的Area,此时IDE会默认为我们添加如下一个AdminAreaRegistration类型。

   1: namespace Artech.MvcApp.Areas.Admin
   2: {
   3:     public class AdminAreaRegistration : AreaRegistration
   4:     {
   5:         public override string AreaName
   6:         {
   7:             get{return "Admin";}
   8:         }
   9:         public override void RegisterArea(AreaRegistrationContext context)
  10:         {
  11:             context.MapRoute("Admin_default", "Admin/{controller}/{action}/{id}",
  12:                 new { action = "Index", id = UrlParameter.Optional }
  13:             );
  14:         }
  15:     }
  16: }

AdminAreaRegistration类型定义在命名空间Artech.MvcApp.Areas.Admin中。现在我们在该Area中添加一个Controller类,其名为HomeController。默认情况下,我们添加的Controller类型和AdminAreaRegistration具有相同的命名空间,但是现在我们刻意将命名空间改为Artech.MvcApp.Areas

   1: namespace Artech.MvcApp.Areas
   2: {
   3:     public class HomeController : Controller
   4:     {
   5:         public ActionResult Index()
   6:         {
   7:             return Content("...");
   8:         }
   9:     }
  10: }

现在我们在浏览器中通过匹配的URL(/Admin/Home/Index)来访问 Area为Admin的HomeController的Index操作,会得到如下图所示的HTTP状态为404的错误。这就是因为在对 Controller类型进行解析的时候是严格按照对应的AreaRegistration所在命名空间来进行的,很显然在这个范围内是不可能找得到对应 的Controller类型的。[源代码从这里下载]

image

四、Controller的激活与URL路由

ASP.NET路由系统是HTTP请求抵达服务端的第一道屏障,它根据注册的路由规则对拦截的请求进行匹配并解析包含目标Controller和 Action名称的路由信息。而当前ControllerBuilder具有用于激活Controller对象的ControllerFactory,我 们现在看看两者是如何结合起来的。

通过《ASP.NET路由系统实现原理:HttpHandler的动态映射》介绍我们知道ASP.NET 路由系统的核心是一个叫做UrlRoutingModule的自定义HttpModule,路由的实现是它通过注册代表当前Web应用的 HttpApplication的PostResolveRequestCache事件对HttpHandler的动态映射来实现的。具体来说,它通过以 RouteTable的静态属性Routes代表的全局路由表对请求进行匹配并得到一个RouteData对象。RouteData具有一个实现了接口 IRouteHandler的属性RouteHandler,通过该属性的GetHttpHandler方法得到最终被映射到当前请求的 HttpHandler

对于ASP.NET MVC应用来说,RouteData的RouteHandler属性类型为MvcRouteHandler,体现在MvcRouteHandler类型上 关于HttpHandler的提供机制基本上(不是完全等同)可以通过如下的代码来表示。MvcRouteHandler维护着一个 ControllerFactory对象,该对象可以在构造函数中指定,如果没有显示指定则直接通过调用当前ControllerBuilder的 GetControllerFactory方法获取。

   1: public class MvcRouteHandler : IRouteHandler
   2: {
   3:     private IControllerFactory _controllerFactory;
   4:      public MvcRouteHandler(): this(ControllerBuilder.Current.GetControllerFactory())
   5:     { }
   6:     public MvcRouteHandler(IControllerFactory controllerFactory)
   7:     {
   8:         _controllerFactory = controllerFactory;
   9:     }
  10:     IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
  11:     {
  12:         string controllerName = (string)requestContext.RouteData.GetRequiredString("controller");
  13:         SessionStateBehavior sessionStateBehavior = _controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
  14:         requestContext.HttpContext.SetSessionStateBehavior(sessionStateBehavior);
  15:
  16:         return new MvcHandler(requestContext);
  17:     }
  18: }

在用于提供HttpHandler的GetHttpHandler方法中,除了返回一个实现了IHttpHandler接口的MvcHandler 对象之外,还需要对当前HTTP上下文的会话状态行为模式进行设置。具体来说,首先通过包含在传入RequestContext的RouteData对象 得到Controller的名称,该名称连同RequestContext对象一起传入ControllerFactory的 GetControllerSessionBehavior方法得到一个类型为SessionStateBehavior的枚举。最后通过 RequestContext得到表示当前HTTP上下文的HttpContextBase对象(实际上是一个HttpContextWrapper对 象)并调用其SetSessionStateBehavior方法。

绍我们知道RouteData中的RouteHandler属性最初来源于对应的Route对象的同名属性,而当我们调用 RouteCollection的扩展方法MapRoute方法时,其内部会直接创建并添加一个Route对象。由于在创建Route对象是并没有显式指 定ControllerFactory,所以通过当前ControllerBuilder的GetControllerFactory方法得到的 ControllerFactory默认被使用。

通过当前ControllerBuilder的GetControllerFactory方法得到的ControllerFactory仅仅用于获 取会话状态行为模式,而MvcHandler真正将它用于创建Controller。MvcHandler中关于对请求处理的逻辑基本上可以通过如下的代 码片断来体现。如下面的代码片断所示,MvcHandler具有一个表示当前请求上下文的RequestContext属性,该属性在构造函数中被初始 化。

   1: public class MvcHandler : IHttpHandler
   2: {
   3:     public RequestContext RequestContext { get; private set; }
   4:     public bool IsReusable
   5:     {
   6:         get { return false; }
   7:     }
   8:     public MvcHandler(RequestContext requestContext)
   9:     {
  10:         this.RequestContext = requestContext;
  11:     }
  12:     public void ProcessRequest(HttpContext context)
  13:     {
  14:         IControllerFactory controllerFactory = ControllerBuilder.Current.GetControllerFactory();
  15:         string controllerName = this.RequestContext.RouteData.GetRequiredString("controller");
  16:         IController controller = controllerFactory.CreateController(this.RequestContext, controllerName);
  17:         try
  18:         {
  19:             controller.Execute(this.RequestContext);
  20:         }
  21:         finally
  22:         {
  23:             controllerFactory.ReleaseController(controller);
  24:         }
  25:     }
  26: }

在ProcessRequest方法中,通过RequestContext对象得到目标Controller的名称,并通过它利用当前 ControllerBuilder创建的ControllerFactory激活Controller对象。在执行了被激活Controller对象的 Execute方法之后调用ControllerFactory的ReleaseController对其进行释放清理工作。

ASP.NET MVC Controller激活系统详解:总体设计
ASP.NET MVC Controller激活系统详解:默认实现
ASP.NET MVC Controller激活系统详解:IoC的应用[上篇]
ASP.NET MVC Controller激活系统详解:IoC的应用[下篇]

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏