• 白话ASP.NET MVC之二:Controller激活系统的概览


          前文简介:我们抽象类路由规则的对象,RouteBase是路由对象的抽象基类,ASP.NET 的路由系统中有唯一一个从RouteBase继承的路由对象,那就是Route类型了。我们注册了路由对象Route,UrlRoutingModule截获请求,把当前请求的Url地址和RouteTable路由表中注册的路由对象一个一个的比较,如果没有找到就返回Null,请求就到此终止了。如果有匹配的路由对象,选择第一个匹配的Route对象,并且根据该Route对象,生成了路由数据RouteData对象,本对象是为了封装成RequestContext对象,RequestContext对象封装了RouteData和HttpContext对象。

          其实RequestContext对象也是作为参数使用的,我们根据Route对象获得了RouteData,该对象有一个RouteHandler属性,这个属性的值在ASP.NET MVC中是MvcRouteHandler。RouteHandler主要的作用是提供用于处理最终请求的HttpHandler对象,代码如下:

    IHttpHandler=RouteData.RouteHandler.GetHttpHandler(requestContext);
    HttpContext.Remap(IHttpHandler);

        上文中我们获取到的RequestContext,将此作为参数调用RouteHandler的GetHttpHandler方法,我们获得了IHttpHandler对象,我们需要把请求交给这个HttpHandler来处理,通过HttpContext.Remap(Handler)实现请求的交接,这个Handler在ASP.NET MVC中就是MvcHandler,我们既然获得了HttpHandler对象,也实现了请求的交接,下一步该做什么呢?

    一、概览

         我们获得了用于处理请求HttpHandler对象,下一步最重要的任务是把Controller对象找到,RouteData对象的Values属性的Controller键和Action键的值只是一个字符串的结果,我们要通过这个名称找到具体的Controller类型实例,才能执行Action方法,给客户想要的应答。这篇文章就是Controller的激活方法的详解。

          ASP.NET MVC系统真的很庞大,也很复杂,如果我们一上来就瀑布式的说,就像流水账一样,估计也不太容易说明白。为了便于理解,我们人为的把ASP.NET MVC这么庞大的系统拆分成相互独立,又有一定联系的多个子系统,然后我们各个击破,理解起来也就简单了,最后在把各个部分整合在一起,理解就全面了。今天我们要说的这一部分暂时叫做Controller激活系统吧,激活系统有两个含义,一是我们要找到我们需要的Controller对象,并实例化;二是我们药缓存他,并要执行它。

        我们先来看看MvcHandler的源码吧,有助于我们的理解,代码如下:

      1         /// <summary>Selects the controller that will handle an HTTP request.</summary>
      2     public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
      3     {
      4         private struct ProcessRequestState
      5         {
      6             internal IAsyncController AsyncController;
      7 
      8             internal IControllerFactory Factory;
      9 
     10             internal RequestContext RequestContext;
     11 
     12             internal void ReleaseController()
     13             {
     14                 this.Factory.ReleaseController(this.AsyncController);
     15             }
     16         }
     17 
     18         private static readonly object _processRequestTag = new object();
     19 
     20         internal static readonly string MvcVersion = MvcHandler.GetMvcVersionString();
     21 
     22         /// <summary>Contains the header name of the ASP.NET MVC version.</summary>
     23         public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version";
     24 
     25         private ControllerBuilder _controllerBuilder;
     26 
     27         internal ControllerBuilder ControllerBuilder
     28         {
     29             get
     30             {
     31                 if (this._controllerBuilder == null)
     32                 {
     33                     this._controllerBuilder = ControllerBuilder.Current;
     34                 }
     35                 return this._controllerBuilder;
     36             }
     37             set
     38             {
     39                 this._controllerBuilder = value;
     40             }
     41         }
     42 
     43         /// <summary>Gets or sets a value that indicates whether the MVC response header is disabled.</summary>
     44         /// <returns>true if the MVC response header is disabled; otherwise, false.</returns>
     45         public static bool DisableMvcResponseHeader
     46         {
     47             get;
     48             set;
     49         }
     50 
     51         /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary>
     52         /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns>
     53         protected virtual bool IsReusable
     54         {
     55             get
     56             {
     57                 return false;
     58             }
     59         }
     60 
     61         /// <summary>Gets the request context.</summary>
     62         /// <returns>The request context.</returns>
     63         public RequestContext RequestContext
     64         {
     65             get;
     66             private set;
     67         }
     68 
     69         /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary>
     70         /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns>
     71         bool IHttpHandler.IsReusable
     72         {
     73             get
     74             {
     75                 return this.IsReusable;
     76             }
     77         }
     78 
     79         /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcHandler" /> class.</summary>
     80         /// <param name="requestContext">The request context.</param>
     81         /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>
     82         public MvcHandler(RequestContext requestContext)
     83         {
     84             if (requestContext == null)
     85             {
     86                 throw new ArgumentNullException("requestContext");
     87             }
     88             this.RequestContext = requestContext;
     89         }
     90 
     91         /// <summary>Adds the version header by using the specified HTTP context.</summary>
     92         /// <param name="httpContext">The HTTP context.</param>
     93         protected internal virtual void AddVersionHeader(HttpContextBase httpContext)
     94         {
     95             if (!MvcHandler.DisableMvcResponseHeader)
     96             {
     97                 httpContext.Response.AppendHeader(MvcHandler.MvcVersionHeaderName, MvcHandler.MvcVersion);
     98             }
     99         }
    100 
    101         /// <summary>Called by ASP.NET to begin asynchronous request processing.</summary>
    102         /// <returns>The status of the asynchronous call.</returns>
    103         /// <param name="httpContext">The HTTP context.</param>
    104         /// <param name="callback">The asynchronous callback method.</param>
    105         /// <param name="state">The state of the asynchronous object.</param>
    106         protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)
    107         {
    108             HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
    109             return this.BeginProcessRequest(httpContext2, callback, state);
    110         }
    111 
    112         /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary>
    113         /// <returns>The status of the asynchronous call.</returns>
    114         /// <param name="httpContext">The HTTP context.</param>
    115         /// <param name="callback">The asynchronous callback method.</param>
    116         /// <param name="state">The state of the asynchronous object.</param>
    117         protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
    118         {
    119             IController controller;
    120             IControllerFactory factory;
    121             this.ProcessRequestInit(httpContext, out controller, out factory);
    122             IAsyncController asyncController = controller as IAsyncController;
    123             if (asyncController != null)
    124             {
    125                 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState)
    126                 {
    127                     IAsyncResult result;
    128                     try
    129                     {
    130                         result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState);
    131                     }
    132                     catch
    133                     {
    134                         innerState.ReleaseController();
    135                         throw;
    136                     }
    137                     return result;
    138                 };
    139                 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState)
    140                 {
    141                     try
    142                     {
    143                         innerState.AsyncController.EndExecute(asyncResult);
    144                     }
    145                     finally
    146                     {
    147                         innerState.ReleaseController();
    148                     }
    149                 };
    150                 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState
    151                 {
    152                     AsyncController = asyncController,
    153                     Factory = factory,
    154                     RequestContext = this.RequestContext
    155                 };
    156                 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext();
    157                 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext);
    158             }
    159             Action action = delegate
    160             {
    161                 try
    162                 {
    163                     controller.Execute(this.RequestContext);
    164                 }
    165                 finally
    166                 {
    167                     factory.ReleaseController(controller);
    168                 }
    169             };
    170             return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag);
    171         }
    172 
    173         /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary>
    174         /// <param name="asyncResult">The asynchronous result.</param>
    175         protected internal virtual void EndProcessRequest(IAsyncResult asyncResult)
    176         {
    177             AsyncResultWrapper.End(asyncResult, MvcHandler._processRequestTag);
    178         }
    179 
    180         private static string GetMvcVersionString()
    181         {
    182             return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);
    183         }
    184 
    185         /// <summary>Processes the request by using the specified HTTP request context.</summary>
    186         /// <param name="httpContext">The HTTP context.</param>
    187         protected virtual void ProcessRequest(HttpContext httpContext)
    188         {
    189             HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
    190             this.ProcessRequest(httpContext2);
    191         }
    192 
    193         /// <summary>Processes the request by using the specified base HTTP request context.</summary>
    194         /// <param name="httpContext">The HTTP context.</param>
    195         protected internal virtual void ProcessRequest(HttpContextBase httpContext)
    196         {
    197             IController controller;
    198             IControllerFactory controllerFactory;
    199             this.ProcessRequestInit(httpContext, out controller, out controllerFactory);
    200             try
    201             {
    202                 controller.Execute(this.RequestContext);
    203             }
    204             finally
    205             {
    206                 controllerFactory.ReleaseController(controller);
    207             }
    208         }
    209 
    210         private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
    211         {
    212             HttpContext current = HttpContext.Current;
    213             if (current != null && ValidationUtility.IsValidationEnabled(current) == true)
    214             {
    215                 ValidationUtility.EnableDynamicValidation(current);
    216             }
    217             this.AddVersionHeader(httpContext);
    218             this.RemoveOptionalRoutingParameters();
    219             string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
    220             factory = this.ControllerBuilder.GetControllerFactory();
    221             controller = factory.CreateController(this.RequestContext, requiredString);
    222             if (controller == null)
    223             {
    224                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]
    225                 {
    226                     factory.GetType(),
    227                     requiredString
    228                 }));
    229             }
    230         }
    231 
    232         private void RemoveOptionalRoutingParameters()
    233         {
    234             RouteValueDictionary values = this.RequestContext.RouteData.Values;
    235             values.RemoveFromDictionary((KeyValuePair<string, object> entry) => entry.Value == UrlParameter.Optional);
    236         }
    237 
    238         /// <summary>Enables processing of HTTP Web requests by a custom HTTP handler that implements the <see cref="T:System.Web.IHttpHandler" /> interface.</summary>
    239         /// <param name="httpContext">An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) that are used to service HTTP requests.</param>
    240         void IHttpHandler.ProcessRequest(HttpContext httpContext)
    241         {
    242             this.ProcessRequest(httpContext);
    243         }
    244 
    245         /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary>
    246         /// <returns>The status of the asynchronous call.</returns>
    247         /// <param name="context">The HTTP context.</param>
    248         /// <param name="cb">The asynchronous callback method.</param>
    249         /// <param name="extraData">The data.</param>
    250         IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    251         {
    252             return this.BeginProcessRequest(context, cb, extraData);
    253         }
    254 
    255         /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary>
    256         /// <param name="result">The asynchronous result.</param>
    257         void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
    258         {
    259             this.EndProcessRequest(result);
    260         }
    261     }
    View Code

       MvcHandler里面这个两个方法定义了Controller激活系统实现的骨架:

     1         /// <summary>Processes the request by using the specified base HTTP request context.</summary>
     2         /// <param name="httpContext">The HTTP context.</param>
     3         protected internal virtual void ProcessRequest(HttpContextBase httpContext)
     4         {
     5             IController controller;
     6             IControllerFactory controllerFactory;
     7             this.ProcessRequestInit(httpContext, out controller, out controllerFactory);
     8             try
     9             {
    10                 controller.Execute(this.RequestContext);
    11             }
    12             finally
    13             {
    14                 controllerFactory.ReleaseController(controller);
    15             }
    16         }
    17 
    18         private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
    19         {
    20             HttpContext current = HttpContext.Current;
    21             if (current != null && ValidationUtility.IsValidationEnabled(current) == true)
    22             {
    23                 ValidationUtility.EnableDynamicValidation(current);
    24             }
    25             this.AddVersionHeader(httpContext);
    26             this.RemoveOptionalRoutingParameters();
    27             string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
    28             factory = this.ControllerBuilder.GetControllerFactory();
    29             controller = factory.CreateController(this.RequestContext, requiredString);
    30             if (controller == null)
    31             {
    32                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]
    33                 {
    34                     factory.GetType(),
    35                     requiredString
    36                 }));
    37             }
    38         }

      代码不是很难,大家一看就能看懂,下一节好好的介绍一些Controller激活系统中所涉及到的各个对象,具体的解析规则要到下一篇文章了,否则文章就太长了。

    二、Controller对象激活详述

          ASP.NET MVC中,我们人为分解的当前系统叫:Controller激活系统,其实这个激活系统里面包含的对象也不是很多,主要有我们要实例化的Controller对象,管理Controller的ControllerFactory对象,管理ControllerFactory的ControllerBuilder对象,这三个主要对象构成了我们的激活系统。

          1、我们先看看Controller类型的定义吧

              我们所说的Controller其实是指实现了IController接口的某个类型实例。Controller是一个可以执行的对象,它的执行体现在对Execute方法上的调用。一说到执行我们是不是就会想到同步执行和异步执行呢,既然Controller是一个可以执行的对象,他会不会也具有同步和异步的执行呢,答案是肯定的,我们先来看看两个接口吧:

    public interface IController
    {
        /// <summary>Executes the specified request context.</summary>
        /// <param name="requestContext">The request context.</param>
        void Execute(RequestContext requestContext);
    }

    这个就是同步版的接口,该接口的命名空间是:System.Web.Mvc,当目标Controller对象被成功激活后,对请求的后续处理和最终响应都通过执行者Execute方法来实现。我们再看看异步版的接口定义吧:

     1 public interface IAsyncController : IController
     2 {
     3     /// <summary>Executes the specified request context.</summary>
     4     /// <returns>The status of the asynchronous operation.</returns>
     5     /// <param name="requestContext">The request context.</param>
     6     /// <param name="callback">The asynchronous callback method.</param>
     7     /// <param name="state">The state.</param>
     8     IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state);
     9 
    10     /// <summary>Ends the asynchronous operation.</summary>
    11     /// <param name="asyncResult">The asynchronous result.</param>
    12     void EndExecute(IAsyncResult asyncResult);
    13 }

      异步版接口定义的命名空间是:System.Web.Mvc.Async,我们看到IAsyncController是实现了IController的接口,Controller的异步执行是通过调用BeginExecute和EndExecute方法来实现的。我们通过Visual Studio创建的ASP.NET MVC项目,我们自定义的Controller实现的基类是Controller类型,不是直接实现以上接口的。我们看看Controller类型的定义吧:

    public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer
    {
    //代码省略
    }

      Controller是抽象类型,实现了很多接口,因为该类的代码太多,所以省略了部分内部代码,这个里面牵扯到另外一个类,就是ControllerBase,

      1 /// <summary>Represents the base class for all MVC controllers.</summary>
      2 public abstract class ControllerBase : IController
      3 {
      4     private readonly SingleEntryGate _executeWasCalledGate = new SingleEntryGate();
      5 
      6     private DynamicViewDataDictionary _dynamicViewDataDictionary;
      7 
      8     private TempDataDictionary _tempDataDictionary;
      9 
     10     private bool _validateRequest = true;
     11 
     12     private IValueProvider _valueProvider;
     13 
     14     private ViewDataDictionary _viewDataDictionary;
     15 
     16     /// <summary>Gets or sets the controller context.</summary>
     17     /// <returns>The controller context.</returns>
     18     public ControllerContext ControllerContext
     19     {
     20         get;
     21         set;
     22     }
     23 
     24     /// <summary>Gets or sets the dictionary for temporary data.</summary>
     25     /// <returns>The dictionary for temporary data.</returns>
     26     public TempDataDictionary TempData
     27     {
     28         get
     29         {
     30             if (this.ControllerContext != null && this.ControllerContext.IsChildAction)
     31             {
     32                 return this.ControllerContext.ParentActionViewContext.TempData;
     33             }
     34             if (this._tempDataDictionary == null)
     35             {
     36                 this._tempDataDictionary = new TempDataDictionary();
     37             }
     38             return this._tempDataDictionary;
     39             }
     40         set
     41         {
     42             this._tempDataDictionary = value;
     43         }
     44     }
     45 
     46     /// <summary>Gets or sets a value that indicates whether request validation is enabled for this request.</summary>
     47     /// <returns>true if request validation is enabled for this request; otherwise, false. The default is true.</returns>
     48     public bool ValidateRequest
     49     {
     50         get
     51         {
     52             return this._validateRequest;
     53         }
     54         set
     55         {
     56             this._validateRequest = value;
     57         }
     58     }
     59 
     60     /// <summary>Gets or sets the value provider for the controller.</summary>
     61     /// <returns>The value provider for the controller.</returns>
     62     public IValueProvider ValueProvider
     63     {
     64         get
     65         {
     66             if (this._valueProvider == null)
     67             {
     68                 this._valueProvider = ValueProviderFactories.Factories.GetValueProvider(this.ControllerContext);
     69             }
     70             return this._valueProvider;
     71         }
     72         set
     73         {
     74             this._valueProvider = value;
     75         }
     76     }
     77 
     78     /// <summary>Gets the dynamic view data dictionary.</summary>
     79     /// <returns>The dynamic view data dictionary.</returns>
     80     [Dynamic]
     81     public dynamic ViewBag
     82     {
     83         [return: Dynamic]
     84         get
     85         {
     86             if (this._dynamicViewDataDictionary == null)
     87             {
     88                 this._dynamicViewDataDictionary = new DynamicViewDataDictionary(() => this.ViewData);
     89             }
     90             return this._dynamicViewDataDictionary;
     91         }
     92     }
     93 
     94     /// <summary>Gets or sets the dictionary for view data.</summary>
     95     /// <returns>The dictionary for the view data.</returns>
     96     public ViewDataDictionary ViewData
     97     {
     98         get
     99         {
    100             if (this._viewDataDictionary == null)
    101             {
    102                 this._viewDataDictionary = new ViewDtaDictionary();
    103             }
    104             return this._viewDataDictionary;
    105         }
    106         set
    107         {
    108             this._viewDataDictionary = value;
    109         }
    110     }
    111     /// <summary>Executes the specified request context.</summary>
    112     /// <param name="requestContext">The request context.</param>
    113     /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>
    114     protected virtual void Execute(RequestContext requestContext)
    115     {
    116         if (requestContext == null)
    117         {
    118             throw new ArgumentNullException("requestContext");
    119         }
    120         if (requestContext.HttpContext == null)
    121         {
    122             throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
    123         }
    124         this.VerifyExecuteCalledOnce();
    125         this.Initialize(requestContext);
    126         using (ScopeStorage.CreateTransientScope())
    127         {
    128             this.ExecuteCore();
    129         }
    130     }
    131 
    132     /// <summary>Executes the request.</summary>
    133     protected abstract void ExecuteCore();
    134 
    135     /// <summary>Initializes the specified request context.</summary>
    136     /// <param name="requestContext">The request context.</param>
    137     protected virtual void Initialize(RequestContext requestContext)
    138     {
    139         this.ControllerContext = new ControllerContext(requestContext, this);
    140     }
    141 
    142     internal void VerifyExecuteCalledOnce()
    143     {
    144         if (!this._executeWasCalledGate.TryEnter())
    145         {
    146             string message = string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBase_CannotHandleMultipleRequests, new object[]
    147             {
    148                 base.GetType()
    149             });
    150             throw new InvalidOperationException(message);
    151         }
    152     }
    153 
    154     /// <summary>Executes the specified request context.</summary>
    155     /// <param name="requestContext">The request context.</param>
    156     void IController.Execute(RequestContext requestContext)
    157     {
    158         this.Execute(requestContext);
    159     }
    160 }

         ControllerBase实现了IController接口,但是没有实现IAsyncController接口,说明ControllerBase是一个同步的基类,之所以单独又增加ControllerBase,个人认为它定义了方法调用的扩展点。ControllerBase是一个抽象类,通过显示的实现了IController的Execute方法,在这个显示实现的方法IController.Execute方法里面又调用了受保护的虚方法Execute,这个受保护的虚方法Execute又在内部调用了抽象方法ExecuteCore,作为ControllerBase的继承者,必须通过实现对抽象方法ExecuteCore来完成对Controller的执行。这个过程就是方法调用的扩展点,而且ControllerBase也抽象所有Controller中都会用到的一些共同的属性,如TempData,ControllerContext,ValueProvider,ViewBag,ViewData等,大家可以细细研究,这里就不细说了,都很简单。

          在ControllerBase里面有一个初始化的方法,这个方法的作用是根据当前Controller对象和当前请求上下文RequestContext创建ControllerContext对象,在请求后续处理的时候很多地方会用到ControllerContext。

    /// <summary>Initializes the specified request context.</summary>
    /// <param name="requestContext">The request context.</param>
    protected virtual void Initialize(RequestContext requestContext)
    {
        this.ControllerContext = new ControllerContext(requestContext, this);
    }

    此方法是虚方法,我们可以扩展他,这个方法的调用点在受保护的虚方法Exuecute中,在调用ExecuteCore之前调用了该方法。

        我们说了接口IController,IAsyncController,也说了抽象类ControllerBase,我们最后对我们要经常使用的基类做一下简述,我们真正在项目中使用的Controller的基类型是Controller类型,Controller实现了IController接口和IAsyncController接口,说明我们自己的Controller是可以同步执行,也可以异步执行的,同时实现了IDisposable接口,说明Controller类型是需要释放的,谁来释放激活的Controller的类型呢,那就是我们接下来要说的一个对象ControllerFactory,这里不多说,下面会解释清楚的。

          最后我们需要说明的,虽然Controller可以同步执行,也可以异步执行,但是是同步还是异步是靠DisableAsyncSupport属性控制的,返回的是布尔值,默认情况是false,意思是指支持异步执行,以下代码说明实际情况。当BeginExecute方法执行的时候,它会根据该属性的值决定调用Execute同步执行Controller,还是调用BeginExecuteCore/EndExecuteCore方法异步执行Controller,换句话说,如果总是希望Controller同步执行,那就要把DisableAsyncSupport属性设置为true。

     1 protected virtual IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state)
     2 {
     3             if (this.DisableAsyncSupport)
     4             {
     5                 Action action = delegate
     6                 {
     7                     this.Execute(requestContext);
     8                 };
     9                 return AsyncResultWrapper.BeginSynchronous(callback, state, action, Controller._executeTag);
    10             }
    11             if (requestContext == null)
    12             {
    13                 throw new ArgumentNullException("requestContext");
    14             }
    15             16             this.Initialize(requestContext);
    17             BeginInvokeDelegate<Controller> beginDelegate = (AsyncCallback asyncCallback, object callbackState, Controller controller) => controller.BeginExecuteCore(asyncCallback, callbackState);
    18             EndInvokeVoidDelegate<Controller> endDelegate = delegate(IAsyncResult asyncResult, Controller controller)
    19             {
    20                 controller.EndExecuteCore(asyncResult);
    21             };
    22             return AsyncResultWrapper.Begin<Controller>(callback, state, beginDelegate, endDelegate, this, Controller._executeTag, -1, null);
    23 }

          2、ControllerContext

            在ASP.NET MVC中我们会遇到很多上下文(Context)的类型,比如:RequestContext,它是对HttpContext和RouteData的封装,今天我们看看ControllerContext,就是指Controller的上下文,再说明白一点就是Controller在执行过程中对需要用到的一些数据的封装对象,它类似一个Facade,里面包含了一些在Controller执行时所要用的一些对象,必须表示请求的HttpContextBase对象。这个类型没什么可说的,很简单,我把反编译的代码也都贴出来了,大家可以自己看。

      1 /// <summary>Encapsulates information about an HTTP request that matches specified <see cref="T:System.Web.Routing.RouteBase" /> and <see cref="T:System.Web.Mvc.ControllerBase" /> instances.</summary>
      2 public class ControllerContext
      3 {
      4     private sealed class EmptyHttpContext : HttpContextBase
      5     {
      6     }
      7 
      8     internal const string ParentActionViewContextToken = "ParentActionViewContext";
      9 
     10     private HttpContextBase _httpContext;
     11 
     12     private RequestContext _requestContext;
     13 
     14     private RouteData _routeData;
     15 
     16     /// <summary>Gets or sets the controller.</summary>
     17     /// <returns>The controller.</returns>
     18     public virtual ControllerBase Controller
     19     {
     20         get;
     21         set;
     22     }
     23 
     24     /// <summary>Gets the display mode.</summary>
     25     /// <returns>The display mode.</returns>
     26     public IDisplayMode DisplayMode
     27     {
     28         get
     29         {
     30             return DisplayModeProvider.GetDisplayMode(this.HttpContext);
     31         }
     32         set
     33         {
     34             DisplayModeProvider.SetDisplayMode(this.HttpContext, value);
     35         }
     36     }
     37 
     38     /// <summary>Gets or sets the HTTP context.</summary>
     39     /// <returns>The HTTP context.</returns>
     40     public virtual HttpContextBase HttpContext
     41     {
     42         get
     43         {
     44             if (this._httpContext == null)
     45             {
     46                 this._httpContext = ((this._requestContext != null) ? this._requestContext.HttpContext : new ControllerContext.EmptyHttpContext());
     47             }
     48             return this._httpContext;
     49         }
     50         set
     51         {
     52             this._httpContext = value;
     53         }
     54     }
     55 
     56     /// <summary>Gets a value that indicates whether the associated action method is a child action.</summary>
     57     /// <returns>true if the associated action method is a child action; otherwise, false.</returns>
     58     public virtual bool IsChildAction
     59     {
     60         get
     61         {
     62             RouteData routeData = this.RouteData;
     63             return routeData != null && routeData.DataTokens.ContainsKey("ParentActionViewContext");
     64         }
     65     }
     66 
     67     /// <summary>Gets an object that contains the view context information for the parent action method.</summary>
     68     /// <returns>An object that contains the view context information for the parent action method.</returns>
     69     public ViewContext ParentActionViewContext
     70     {
     71         get
     72         {
     73             return this.RouteData.DataTokens["ParentActionViewContext"] as ViewContext;
     74         }
     75     }
     76 
     77     /// <summary>Gets or sets the request context.</summary>
     78     /// <returns>The request context.</returns>
     79     public RequestContext RequestContext
     80     {
     81         get
     82         {
     83             if (this._requestContext == null)
     84             {
     85                 HttpContextBase httpContext = this.HttpContext ?? new ControllerContext.EmptyHttpContext();
     86                 RouteData routeData = this.RouteData ?? new RouteData();
     87                 this._requestContext = new RequestContext(httpContext, routeData);
     88             }
     89             return this._requestContext;
     90         }
     91         set
     92         {
     93             this._requestContext = value;
     94         }
     95     }
     96 
     97     /// <summary>Gets or sets the URL route data.</summary>
     98     /// <returns>The URL route data.</returns>
     99     public virtual RouteData RouteData
    100     {
    101         get
    102         {
    103             if (this._routeData == null)
    104             {
    105                 this._routeData = ((this._requestContext != null) ? this._requestContext.RouteData : new RouteData());
    106             }
    107             return this._routeData;
    108         }
    109         set
    110         {
    111             this._routeData = value;
    112         }
    113     }
    114 
    115     /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class.</summary>
    116     public ControllerContext()
    117     {
    118     }
    119 
    120     /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class by using the specified controller context.</summary>
    121     /// <param name="controllerContext">The controller context.</param>
    122     /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerContext" /> parameter is null.</exception>
    123     protected ControllerContext(ControllerContext controllerContext)
    124     {
    125         if (controllerContext == null)
    126         {
    127             throw new ArgumentNullException("controllerContext");
    128         }
    129         this.Controller = controllerContext.Controller;
    130         this.RequestContext = controllerContext.RequestContext;
    131     }
    132 
    133     /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class by using the specified HTTP context, URL route data, and controller.</summary>
    134     /// <param name="httpContext">The HTTP context.</param>
    135     /// <param name="routeData">The route data.</param>
    136     /// <param name="controller">The controller.</param>
    137     public ControllerContext(HttpContextBase httpContext, RouteData routeData, ControllerBase controller) : this(new RequestContext(httpContext, routeData), controller)
    138     {
    139     }
    140 
    141     /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class by using the specified request context and controller.</summary>
    142     /// <param name="requestContext">The request context.</param>
    143     /// <param name="controller">The controller.</param>
    144     /// <exception cref="T:System.ArgumentNullException">One or both parameters are null.</exception>
    145     public ControllerContext(RequestContext requestContext, ControllerBase controller)
    146     {
    147         if (requestContext == null)
    148         {
    149             throw new ArgumentNullException("requestContext");
    150         }
    151         if (controller == null)
    152         {
    153             throw new ArgumentNullException("controller");
    154         }
    155         this.RequestContext = requestContext;
    156         this.Controller = controller;
    157     }
    158 }

          3、ControllerFactory

              我们的Controller对象有了,为了解耦和可扩展性,ASP.NET MVC 框架中不可能直接new一个Controller的实例对象吧,我们在MvcHandler的代码中也看到了是基于接口编程的,所以需要提供一种机制,来提供我们要得到的Controller对象。既然不能直接new一个Controller出来,那我们就封装new的过程,用工厂来代替吧。对了,就是工厂,这个工厂就是ControllerFactory,从名字上可以看出它的意思,创建Controller对象的工厂。ASP.NET MVC是可以扩展的,可以自定义的,我们的ControllerFactory工厂也不例外,所以我们就产生了ControllerFactory工厂的接口,接口的名字是IControllerFactory,所有的ControllerFactory都必须实现该接口。接口定义如下:

     1       /// <summary>Defines the methods that are required for a controller factory.</summary>
     2     public interface IControllerFactory
     3     {
     4         /// <summary>Creates the specified controller by using the specified request context.</summary>
     5         /// <returns>The controller.</returns>
     6         /// <param name="requestContext">The request context.</param>
     7         /// <param name="controllerName">The name of the controller.</param>
     8         IController CreateController(RequestContext requestContext, string controllerName);
     9 
    10         /// <summary>Gets the controller's session behavior.</summary>
    11         /// <returns>The controller's session behavior.</returns>
    12         /// <param name="requestContext">The request context.</param>
    13         /// <param name="controllerName">The name of the controller whose session behavior you want to get.</param>
    14         SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
    15 
    16         /// <summary>Releases the specified controller.</summary>
    17         /// <param name="controller">The controller.</param>
    18         void ReleaseController(IController controller);
    19     }

       IControllerFactory接口是对管理Controller对象的工厂的抽象,怎么管理呢,无非就是我要创建一个Controller就能创建一个Controller。我们创建好了Controller,不使用的情况就要能销毁它,能创建和能销毁就构成了IControllerFactory的主要功能,CreateController用于创建Controller对象实例,ReleaseController方法用于销毁不用的Controller对象,因为我们的Controller实现了IDisposable接口。想起来了吗?因为我们自定义的Controller都继承了抽象基类Controller类型,该抽象Controller基类实现了IDisposable接口。

          IControllerFactory接口还有一个方法GetControllerSessionBehavior,用于管理Controller的会话状态,返回的类型为SessionStateBehavior枚举值,它有四个枚举值:Default(使用默认的ASP.NET逻辑来确定请求的会话行为),Required(为请求启用完全的读和写会话状态的行为),ReadOnly(为请求启用只读的会话状态),Disabled(禁用会话状态)。说起来话长,在System.Web.SessionState命名空间下有两个接口定义,分别是IRequiresSessionState(实现本接口,HttpHandler采用Required会话模式)和IReadOnlySessionState(实现本接口,HttpHandler采用ReadOnly会话模式)类型,这个两个接口是标记接口,没有定义具体的方法。我们在看看我们的MvcHandler类型的定义吧,代码如下:

    public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState

         具体采用何种会话状态行为取决于当前Http请求上下文(HttpContext的静态属性Current表示的对象),对于ASP.NET 3.0及其以前的版本,我们不能对当前的Http上下文的会话状态行为模式进行修改,在ASP.NET 4.0中为HttpContext对象定义了一个SetSessionStateBehavior方法,此方法也在HttpContextBase定义了,HttpContextBase的子类HttpContextWrapper重写了这个方法,在内部调用HttpContext的同名方法来设置当前请求的会话状态模式。

          4、ControllerBuilder

            我们有了Controller,可以通过IController,IAsyncController,ControllerBase或者Controller抽象基类来自定义我们自己的Controller基类型,我们也有了IControllerFactory接口,可以管理Controller对象,也可以定义我们自己的ControllerFactory来取代系统的默认实现。问题来了,我们自定义了ControllerFactory对象,但是如何把我们自定义的ControllerFactory放到ASP.NET MVC框架中呢,那就需要我们这个类型了,它的名字就是ControllerBuilder。

           代码最有说服力,我们先上代码吧,源码如下:

      1      /// <summary>Represents a class that is responsible for dynamically building a controller.</summary>
      2     public class ControllerBuilder
      3     {
      4         private static ControllerBuilder _instance = new ControllerBuilder();
      5 
      6         private Func<IControllerFactory> _factoryThunk = () => null;
      7 
      8         private HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
      9 
     10         private IResolver<IControllerFactory> _serviceResolver;
     11 
     12         /// <summary>Gets the current controller builder object.</summary>
     13         /// <returns>The current controller builder.</returns>
     14         public static ControllerBuilder Current
     15         {
     16             get
     17             {
     18                 return ControllerBuilder._instance;
     19             }
     20         }
     21 
     22         /// <summary>Gets the default namespaces.</summary>
     23         /// <returns>The default namespaces.</returns>
     24         public HashSet<string> DefaultNamespaces
     25         {
     26             get
     27             {
     28                 return this._namespaces;
     29             }
     30         }
     31 
     32         /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerBuilder" /> class.</summary>
     33         public ControllerBuilder() : this(null)
     34         {
     35         }
     36 
     37         internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
     38         {
     39             IResolver<IControllerFactory> arg_6A_1 = serviceResolver;
     40             if (serviceResolver == null)
     41             {
     42                 arg_6A_1 = new SingleServiceResolver<IControllerFactory>(() => this._factoryThunk(), new DefaultControllerFactory
     43                 {
     44                     ControllerBuilder = this
     45                 }, "ControllerBuilder.GetControllerFactory");
     46             }
     47             this._serviceResolver = arg_6A_1;
     48         }
     49 
     50         /// <summary>Gets the associated controller factory.</summary>
     51         /// <returns>The controller factory.</returns>
     52         public IControllerFactory GetControllerFactory()
     53         {
     54             return this._serviceResolver.Current;
     55         }
     56 
     57         /// <summary>Sets the specified controller factory.</summary>
     58         /// <param name="controllerFactory">The controller factory.</param>
     59         /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerFactory" /> parameter is null.</exception>
     60         public void SetControllerFactory(IControllerFactory controllerFactory)
     61         {
     62             if (controllerFactory == null)
     63             {
     64                 throw new ArgumentNullException("controllerFactory");
     65             }
     66             this._factoryThunk = (() => controllerFactory);
     67         }
     68 
     69         /// <summary>Sets the controller factory by using the specified type.</summary>
     70         /// <param name="controllerFactoryType">The type of the controller factory.</param>
     71         /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerFactoryType" /> parameter is null.</exception>
     72         /// <exception cref="T:System.ArgumentException">The controller factory cannot be assigned from the type in the <paramref name="controllerFactoryType" /> parameter.</exception>
     73         /// <exception cref="T:System.InvalidOperationException">An error occurred while the controller factory was being set.</exception>
     74         public void SetControllerFactory(Type controllerFactoryType)
     75         {
     76             if (controllerFactoryType == null)
     77             {
     78                 throw new ArgumentNullException("controllerFactoryType");
     79             }
     80             if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType))
     81             {
     82                 throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_MissingIControllerFactory, new object[]
     83                 {
     84                     controllerFactoryType
     85                 }), "controllerFactoryType");
     86             }
     87             this._factoryThunk = delegate
     88             {
     89                 IControllerFactory result;
     90                 try
     91                 {
     92                     result = (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
     93                 }
     94                 catch (Exception innerException)
     95                 {
     96                     throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_ErrorCreatingControllerFactory, new object[]
     97                     {
     98                         controllerFactoryType
     99                     }), innerException);
    100                 }
    101                 return result;
    102             };
    103         }
    104     }

       源码不是很多,我贴出了完整的代码。

          ControllerBuilder定义了一个静态只读属性Current,返回当前使用的ControllerBuilder对象。该类型有一个返回类型为IControllerFactory的方法GetControllerFactory,这个方法用于获取当前注册的ControllerFactory对象。ControllerBuilder类型还有两个重载的SetControllerFactory方法,这两个方法的主要作用是把我们自定义的ControllerFactory注册到ASP.NET MVC框架中去。但是这两个方法有些不同,参数类型为IControllerFactory的SetControllerFactory方法直接注册ControllerFactory实例对象,而参数类型为Type的SetControllerFactory方法注册的ControllerFactory的类型。

          如果我们注册的是ControllerFactory类型的话,那么GetControllerFactory方法在获取ControllerFactory对象的时候都要通过反射来获得,也就是说针对GetControllerFactory方法的每次调用都会伴随着ControllerFactory的实例化,ASP.NET MVC框架不会对实例化的ControllerFactory进行缓存。如果我们注册的是ControllerFactory对象实例,针对GetControllerFactory方法来说是直接返回注册的对象,就性能而言,注册ControllerFactory对象比较好。

          命名空间

           如果在多个命名空间下定义了多个同名的Controller类型,如果只是按着名称来匹配就会导致激活系统无法确定具体的Controller的类型而抛出异常。为了解决这个问题,我们必须为定义了同名的Controller类型的命名空间设置不同的优先级。

          ASP.NET MVC的Controller的激活系统为我们提供了两种提升命名空间优先级的方法。第一种方法就是通过调用RouteCollectionExtensions的扩展方法MapRoute的时候提供命名空间列表。此种方式指定的命名空间列表会保存在Route路由对象的DataTokens属性中,对应的Key是Namespaces。第二种方式就是把命名空间列表添加到当前的ControllerBuilder类型的默认命名空间列表中。这些命名空间列表存放在返回类型为HashSet<string>的DefaultNamespaces属性中。这两种方法的优先级第一种方法更高。

         对于Area的命名空间来说,如果我们在调用AreaRegistrationContext对象的MapRoute方法时提供了命名空间,该命名空间会作为Route对象的命名空间,如果没有提供,则AreaRegistration类型所在的命名空间,再加上“.*”作为Route对象的命名空间。当我们调用AreaRegistrationContext的MapRoute方法的时候,会在Route对象的DataTokens属性里增加一个Key为UseNamespaceFallback的变量,它表示是否采用后备的命名空间来解析Controller类型。如果Route对象有命名空间,该值就是False,否则就是true。

         解析过程是,ASP.NET MVC会先使用RouteData包含的命名空间,如果解析失败,它会从RouteData对象的DataTokens属性中取出Key为UseNamespaceFallback的值,如果该值为true或者不存在,就使用ControllerBuidler的默认命名空列表来解析。如果UseNamespaceFallback的值为false,就不实用后备的命名空间,如果没找到就会抛出异常。

    三、激活Controller类型的缓存和释放

         一个比较大的系统里面,可能所涉及到的Controller类型成千上万,如果每次都要通过反射的方式及时获取,那对性能的影响是很客观的,ASP.NET MVC框架组想到了这一点,针对Controller和AreaRegistration都实现了彻底的缓存,这个缓存是把所有解析出来的类型持久化到物理文件中,两个物理文件的名称分别是:MVC-ControllerTypeCache.xml和MVC-AreaRegistrationTypeCache.xml,具体的目录在%windir%Microsoft.NetFrameworkv{version}Temporary ASP.NET Files oot....UserCache。

         当接收到web应用被启动后的第一个请求的时候,Controller激活系统会读取这些文件,通过反序列化生成List<Type>对象。

        MVC-ControllerTypeCache.xml代码如下:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <!--This file is automatically generated. Please do not modify the contents of this file.-->
    
    -<typeCache mvcVersionId="cc73190b-ab9d-435c-8315-10ff295c572a" lastModified="2017/6/23 18:34:17">
    
    
    -<assembly name="MVCTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    
    
    -<module versionId="1949a001-c30a-4d56-ac65-d1714f608b76">
    
    <type>MVCTest.Controllers.AccountController</type>
    
    <type>MVCTest.Controllers.HomeController</type>
    
    <type>MVCTest.Controllers.ManageController</type>
    
    </module>
    
    </assembly>
    
    </typeCache>

      Controller的释放很简单,直接上代码吧。

    /// <summary>Releases the specified controller.</summary>
    /// <param name="controller">The controller to release.</param>
    public virtual void ReleaseController(IController controller)
    {
        IDisposable disposable = controller as IDisposable;
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }

      由于所有的Controller类型都实现了IDisposable接口,所以我们可以直接调用当前Controller对象的Dispose方法即可。

    四、小结

       好了,这篇文章就先写到这里吧,Controller的激活系统还没有完,今天只是从总体上来讲一下,内容很多,很难在一片文章里把所有东西都写到。这个概览先给大家一个整体上的感觉吧,下一篇文章就具体写一下Controller的激活解析过程。

       写一写很不错,把自己的理解写出来,现在更清晰了,也希望高手来指正和赐教。

  • 相关阅读:
    linux线程池简单实例
    OpenCv ROI操作
    OpenCV特征点检测------ORB特征
    opencv中的SIFT,SURF,ORB,FAST 特征描叙算子比较
    OpenCv学习--图像画框标记rectangle
    MATLAB 图片鼠标画框保存
    不错的Django博客
    windows下使用Redis
    前端页面模板bootstrap
    Jenkins启动Tomcat时提示Neither the JAVA_HOME nor the JRE_HOME environment variable is defined
  • 原文地址:https://www.cnblogs.com/PatrickLiu/p/7326916.html
Copyright © 2020-2023  润新知