• 白话ASP.NET MVC之三:Controller是如何解析出来的


        我们在上一篇文章中介绍Controller激活系统中所涉及到的一些类型,比如有关Controller类型的相关定义类型就包括了IController类型,IAsyncController类型,ControllerBase抽象类型和我们最终要使用的抽象类型Controller,这是ASP.NET MVC 框架中和Controller本身定义相关的类型。其他辅助类型,包括管理Controller的类型ControllerFactory,这个工厂负责Controller的生产和销毁。我们还涉及到另一个辅助类型,用于把系统默认定义的或者自定义的ControllerFactory注册到ASP.NET MVC框架中的类型ControllerBuilder。

         Controller类型、ControllerFactory类型和ControllerBuilder类型,他们之间的关系可以描述为:ControllerBuilder是面向客户的,或者说是程序员和ASP.NET MVC框架之间的桥梁。我们通过ControllerBuilder类型的SetControllerFactory方法把我们自定义的ControllerFactory类型实例注册到ASP.NET MVC框架中,ControllerFactory类型用于管理Controller类型实例,其实也就是说ControllerFactory类型就是ASP.NET MVC框架中的一个扩展点。

        我们今天主要讲Controller是怎么解析出来的,之所以把这一部分分开写,因为合在一起太长了,也说的不详细,如果大家对以上说的不太清楚,可以查看《白话ASP.NET MVC之二:Controller激活系统的概览》,  该文对ASP.NET MVC框架中所提到的Controlelr激活系统所涉及的类型有详细的介绍。

    一、“路由系统”和“激活系统”是怎么关联起来的

         上一篇文章有过讲述,我们在这里简单说一下。ASP.NET 的路由系统是建立在一个叫做UrlRoutingModule的HttpModule组件上的,针对请求的路由解析是通过注册HttpApplication对象的PostResolveRequestCache事件来实现的,为当前的请求动态映射到一个HttpHandler类型上,最终由该HttpHandler接管请求并处理。我们来看看UrlRoutingModule类型的代码吧。

     1 [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
     2 public class UrlRoutingModule : IHttpModule
     3 {
     4     private static readonly object _contextKey = new object();
     5 
     6     private static readonly object _requestDataKey = new object();
     7 
     8     private RouteCollection _routeCollection;
     9 
    10     public RouteCollection RouteCollection
    11     {
    12         get
    13         {
    14             if (this._routeCollection == null)
    15             {
    16                 this._routeCollection = RouteTable.Routes;
    17             }
    18             return this._routeCollection;
    19         }
    20         set
    21         {
    22             this._routeCollection = value;
    23         }
    24     }
    25 
    26     protected virtual void Dispose()
    27     {
    28     }
    29 
    30     protected virtual void Init(HttpApplication application)
    31     {
    32         if (application.Context.Items[UrlRoutingModule._contextKey] != null)
    33         {
    34             return;
    35         }
    36         application.Context.Items[UrlRoutingModule._contextKey] = UrlRoutingModule._contextKey;
    37         application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
    38     }
    39 
    40     private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
    41     {
    42         HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context);
    43         this.PostResolveRequestCache(context);
    44     }
    45 
    46     [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
    47     public virtual void PostMapRequestHandler(HttpContextBase context)
    48     {
    49     }
    50 
    51     public virtual void PostResolveRequestCache(HttpContextBase context)
    52     {
    53         RouteData routeData = this.RouteCollection.GetRouteData(context);
    54         if (routeData == null)
    55         {
    56             return;
    57         }
    58         IRouteHandler routeHandler = routeData.RouteHandler;
    59         if (routeHandler == null)
    60         {
    61             throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
    62         }
    63         if (routeHandler is StopRoutingHandler)
    64         {
    65             return;
    66         }
    67         RequestContext requestContext = new RequestContext(context, routeData);
    68         context.Request.RequestContext = requestContext;
    69         IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
    70         if (httpHandler == null)
    71         {
    72             throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[]
    73             {
    74                 routeHandler.GetType()
    75             }));
    76         }
    77         if (!(httpHandler is UrlAuthFailureHandler))
    78         {
    79             context.RemapHandler(httpHandler);
    80             return;
    81         }
    82         if (FormsAuthenticationModule.FormsAuthRequired)
    83         {
    84             UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
    85             return;
    86         }
    87         throw new HttpException(401, SR.GetString("Assess_Denied_Description3"));
    88     }
    89 
    90     void IHttpModule.Dispose()
    91     {
    92         this.Dispose();
    93     }
    94 
    95     void IHttpModule.Init(HttpApplication application)
    96     {
    97         this.Init(application);
    98     }
    99 }

      具体来说,该组件通过以RouteTable的静态属性Routes表示的路由表针对当前请求实施路由解析,如果有匹配,就会根据路由对象Route来生成RouteData路由数据对象,然后我们借助RouteData对象的RouteHandler属性获取想要的HttpHandler对象。在默认情况下这个RouteHandler属性所代表的对象是MvcRouteHandler。翠花,上代码:

     1 /// <summary>Creates an object that implements the IHttpHandler interface and passes the request context to it.</summary>
     2 public class MvcRouteHandler : IRouteHandler
     3 {
     4     private IControllerFactory _controllerFactory;
     5 
     6     /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcRouteHandler" /> class.</summary>
     7     public MvcRouteHandler()
     8     {
     9     }
    10 
    11     /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcRouteHandler" /> class using the specified factory controller object.</summary>
    12     /// <param name="controllerFactory">The controller factory.</param>
    13     public MvcRouteHandler(IControllerFactory controllerFactory)
    14     {
    15         this._controllerFactory = controllerFactory;
    16     }
    17 
    18     /// <summary>Returns the HTTP handler by using the specified HTTP context.</summary>
    19     /// <returns>The HTTP handler.</returns>
    20     /// <param name="requestContext">The request context.</param>
    21     protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
    22     {
    23             requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
    24         return new MvcHandler(requestContext);
    25     }
    26 
    27     /// <summary>Returns the session behavior.</summary>
    28     /// <returns>The session behavior.</returns>
    29     /// <param name="requestContext">The request context.</param>
    30     protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
    31     {
    32         string text = (string)requestContext.RouteData.Values["controller"];
    33         if (string.IsNullOrWhiteSpace(text))
    34         {
    35             throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);
    36         }
    37         IControllerFactory controllerFactory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
    38         return controllerFactory.GetControllerSessionBehavior(requestContext, text);
    39     }
    40 
    41     /// <summary>Returns the HTTP handler by using the specified request context.</summary>
    42     /// <returns>The HTTP handler.</returns>
    43     /// <param name="requestContext">The request context.</param>
    44     IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
    45     {
    46         return this.GetHttpHandler(requestContext);
    47     }
    48 }

      在该类型里面包含了一个IControllerFactory类型成员字段,这个接口类型是所有ControllerFactory都要必须实现的接口,否则就不叫Controller的工厂了。MvcRouteHandler类型有两个构造函数,无参的没的说,另一个需要传递一个IControllerFactory类型的参数,这个参数用于初始化MvcRouteHandler类型内部包含的类型为IControllerFactory的_controllerFactory字段。当我们构造MvcRouteHandler实例的时候,如果我们调用了无参的构造函数,它会在内部使用ControllerBuilder.Current.GetControllerFactory()方法来获取我们通过ControllerBuilder类型注册的IControllerFactory类型的实例,代码很明显:

    IControllerFactory controllerFactory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();

      MvcRouteHandler实现了IRouteHandler接口,目的只有一个,提供后续的HttpHandler,IRouteHandler接口定义如下:

    public interface IRouteHandler
    {
        IHttpHandler GetHttpHandler(RequestContext requestContext);
    }

       MvcRouteHandler会给我们直接返回MvcHandler对象,这个对象用于处理请求,包括激活Controler对象,代码最有说服力,这份代码,上篇文章也贴过,现在也贴一下把,上代码:
      

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


      MvcHandler类型的BeginProcessRequest方法用来处理请求,包括激活Controller实例等。由于MvcHandler类型同时实现了IHttpHandler接口和IHttpAsyncHandler接口,所以他总是异步方式去执行(调用BeginProcessRequest/EndProcessRequest方法)。BeginProcessRequest方法在执行的时候通过RequestContext对象的RouteData属性获得Controller的名字,然后在通过ControllerBuilder获得ControllerFactory对象,然后用Controller的名字和ControllerFactory对象来激活目标Controller实例。如果Controller类型实现了IAsyncController接口,那就以异步方式执行Controller,否则以同步方式执行。Controller对象成功执行后,MvcHandler对象会调用ControllerFactory对象ReleaseController方法来销毁Controller实例对象。

         我们小结一下,ASP.NET MVC的路由系统和Controller的激活系统是通过这些对象关联起来的:请求Url ------->Route------->RouteData------->RouteHandler(MvcRouteHandler)-------->MvcRouteHandler------>MvcHandler,通过这些对象就能串起来了。

    二、Controller的详细解析过程

         我先来简述一下Controller解析的原理吧。Controller实例对象的解析是通过实现了IControllerFactory接口的ControllerFactory对象实现的,ControllerFactory是怎么来的呢?是通过调用ControllerBuilder的SetControllerFactory方法实现对ControllerFactory类型或者实例对象的注册。如果我们没有调用ControllerBuilder的SetControllerFactory方法对象ControllerFactory类型或者实例显示注册,系统会使用默认的ControllerFactory来完成对Controller对象的解析,这个对象就是DefaultControllerFactory类型,该类型的实现正好反映了ASP.NET MVC框架对Controller实例的激活采取的默认策略。今天我们就看看DefaultControllerFactory类型是如何把Controller对象激活的,这也是Controller激活系统的默认实现,我们可以扩展ControllerFactory类型,实现自定义的Controller激活策略。

         我把代码流程写一下,在MvcHandler类型里面有两个方法,一个方法是:BeginProcessRequest(HttpContextBase httpContext,AsyncCallback callback,object state),该方法用于对请求进行处理;第二个方法是:ProcessRequestInit(HttpContextbase httpContext,out IController controller,out IControllerFactory controllerFactory),该方法就是定义了激活Controller算法的骨架,上代码吧,代码最无二意,我们先看BeginProcessRequest的代码:

     1         /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary>
     2         /// <returns>The status of the asynchronous call.</returns>
     3         /// <param name="httpContext">The HTTP context.</param>
     4         /// <param name="callback">The asynchronous callback method.</param>
     5         /// <param name="state">The state of the asynchronous object.</param>
     6         protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
     7         {
     8             IController controller;
     9             IControllerFactory factory;
    10             this.ProcessRequestInit(httpContext, out controller, out factory);
    11             IAsyncController asyncController = controller as IAsyncController;
    12             if (asyncController != null)
    13             {
    14                 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState)
    15                 {
    16                     IAsyncResult result;
    17                     try
    18                     {
    19                         result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState);
    20                     }
    21                     catch
    22                     {
    23                         innerState.ReleaseController();
    24                         throw;
    25                     }
    26                     return result;
    27                 };
    28                 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState)
    29                 {
    30                     try
    31                     {
    32                         innerState.AsyncController.EndExecute(asyncResult);
    33                     }
    34                     finally
    35                     {
    36                         innerState.ReleaseController();
    37                     }
    38                 };
    39                 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState
    40                 {
    41                     AsyncController = asyncController,
    42                     Factory = factory,
    43                     RequestContext = this.RequestContext
    44                 };
    45                 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext();
    46                 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext);
    47             }
    48             Action action = delegate
    49             {
    50                 try
    51                 {
    52                     controller.Execute(this.RequestContext);
    53                 }
    54                 finally
    55                 {
    56                     factory.ReleaseController(controller);
    57                 }
    58             };
    59             return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag);
    60         }


       在这个方法里面根据解析出来的Controller的类型来执行,如果是异步的Controller那就异步执行,否则就同步执行。在该方法里面,第三行,标红色的方法就是定义解析和执行Controller的算法骨架,就是我们要贴代码的第二个方法ProcessRequestInit,源码如下:
     

     1         private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
     2         {
     3             HttpContext current = HttpContext.Current;
     4             if (current != null && ValidationUtility.IsValidationEnabled(current) == true)
     5             {
     6                 ValidationUtility.EnableDynamicValidation(current);
     7             }
     8             this.AddVersionHeader(httpContext);
     9             this.RemoveOptionalRoutingParameters();
    10             string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
    11             factory = this.ControllerBuilder.GetControllerFactory();
    12             controller = factory.CreateController(this.RequestContext, requiredString);
    13             if (controller == null)
    14             {
    15                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]
    16                 {
    17                     factory.GetType(),
    18                     requiredString
    19                 }));
    20             }
    21         }

         这个方法主要是获取ControllerFactory实例,根据获得的ControllerFactory对象激活Controller对象,红色标注的代码就是核心关键点。说明一点,这个方法只是定义了激活Controller算法的骨架,具体的实现在DefaultControllerFactory类型中。代码很简单,我相信大家看的清楚。

           DefaultControllerFactory会先通过“MVC-ControllerTypeCache.xml”文件来加载Controller的类型,如果没有找到有效的数据才会通过调用BuildManager的静态方法GetReferencedAssemblies获取到系统所使用到的所有程序集,然后针对每个程序集通过反射的方式获得所有实现了IController接口的类型,并XML文件的形式缓存起来,缓存的文件名就是“MVC-ControllerTypeCache.xml”。然后我们把Controller的名称和命名空间作为匹配条件去查找对应的Controller类型。当我们获得了符合标准的真是的Controller类型后,DefaultControllerFactory对象通过反射的方式创建Controller类型的实例对象。解析逻辑不复杂,但是代码不少。首先我说明一点,把代码全部贴出来不是为了占篇幅,而是为了大家能看的更明白,我把完整的源码贴出来,大家可以仔细体会一下。

      1 /// <summary>Represents the controller factory that is registered by default.</summary>
      2     public class DefaultControllerFactory : IControllerFactory
      3     {
      4         private class DefaultControllerActivator : IControllerActivator
      5         {
      6             private Func<IDependencyResolver> _resolverThunk;
      7 
      8             public DefaultControllerActivator() : this(null)
      9             {
     10             }
     11 
     12             public DefaultControllerActivator(IDependencyResolver resolver)
     13             {
     14                 if (resolver == null)
     15                 {
     16                     this._resolverThunk = (() => DependencyResolver.Current);
     17                     return;
     18                 }
     19                 this._resolverThunk = (() => resolver);
     20             }
     21 
     22             public IController Create(RequestContext requestContext, Type controllerType)
     23             {
     24                 IController result;
     25                 try
     26                 {
     27                     result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
     28                 }
     29                 catch (Exception innerException)
     30                 {
     31                     throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[]
     32                     {
     33                         controllerType
     34                     }), innerException);
     35                 }
     36                 return result;
     37             }
     38         }
     39 
     40         private static readonly ConcurrentDictionary<Type, SessionStateBehavior> _sessionStateCache = new ConcurrentDictionary<Type, SessionStateBehavior>();
     41 
     42         private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache();
     43 
     44         private IBuildManager _buildManager;
     45 
     46         private IResolver<IControllerActivator> _activatorResolver;
     47 
     48         private IControllerActivator _controllerActivator;
     49 
     50         private ControllerBuilder _controllerBuilder;
     51 
     52         private ControllerTypeCache _instanceControllerTypeCache;
     53 
     54         private IControllerActivator ControllerActivator
     55         {
     56             get
     57             {
     58                 if (this._controllerActivator != null)
     59                 {
     60                     return this._controllerActivator;
     61                 }
     62                 this._controllerActivator = this._activatorResolver.Current;
     63                 return this._controllerActivator;
     64             }
     65         }
     66 
     67         internal IBuildManager BuildManager
     68         {
     69             get
     70             {
     71                 if (this._buildManager == null)
     72                 {
     73                     this._buildManager = new BuildManagerWrapper();
     74                 }
     75                 return this._buildManager;
     76             }
     77             set
     78             {
     79                 this._buildManager = value;
     80             }
     81         }
     82 
     83         internal ControllerBuilder ControllerBuilder
     84         {
     85             get
     86             {
     87                 return this._controllerBuilder ?? ControllerBuilder.Current;
     88             }
     89             set
     90             {
     91                 this._controllerBuilder = value;
     92             }
     93         }
     94 
     95         internal ControllerTypeCache ControllerTypeCache
     96         {
     97             get
     98             {
     99                 return this._instanceControllerTypeCache ?? DefaultControllerFactory._staticControllerTypeCache;
    100             }
    101             set
    102             {
    103                 this._instanceControllerTypeCache = value;
    104             }
    105         }
    106 
    107         /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class.</summary>
    108         public DefaultControllerFactory() : this(null, null, null)
    109         {
    110         }
    111 
    112         /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class using a controller activator.</summary>
    113         /// <param name="controllerActivator">An object that implements the controller activator interface.</param>
    114         public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null)
    115         {
    116         }
    117 
    118         internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
    119         {
    120             if (controllerActivator != null)
    121             {
    122                 this._controllerActivator = controllerActivator;
    123                 return;
    124             }
    125             IResolver<IControllerActivator> arg_44_1 = activatorResolver;
    126             if (activatorResolver == null)
    127             {
    128                 arg_44_1 = new SingleServiceResolver<IControllerActivator>(() => null, new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory constructor");
    129             }
    130             this._activatorResolver = arg_44_1;
    131         }
    132 
    133         internal static InvalidOperationException CreateAmbiguousControllerException(RouteBase route, string controllerName, ICollection<Type> matchingTypes)
    134         {
    135             StringBuilder stringBuilder = new StringBuilder();
    136             foreach (Type current in matchingTypes)
    137             {
    138                 stringBuilder.AppendLine();
    139                 stringBuilder.Append(current.FullName);
    140             }
    141             Route route2 = route as Route;
    142             string message;
    143             if (route2 != null)
    144             {
    145                 message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl, new object[]
    146                 {
    147                     controllerName,
    148                     route2.Url,
    149                     stringBuilder,
    150                     Environment.NewLine
    151                 });
    152             }
    153             else
    154             {
    155                 message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl, new object[]
    156                 {
    157                     controllerName,
    158                     stringBuilder,
    159                     Environment.NewLine
    160                 });
    161             }
    162             return new InvalidOperationException(message);
    163         }
    164 
    165         private static InvalidOperationException CreateDirectRouteAmbiguousControllerException(ICollection<Type> matchingTypes)
    166         {
    167             StringBuilder stringBuilder = new StringBuilder();
    168             foreach (Type current in matchingTypes)
    169             {
    170                 stringBuilder.AppendLine();
    171                 stringBuilder.Append(current.FullName);
    172             }
    173             string message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_DirectRouteAmbiguous, new object[]
    174             {
    175                 stringBuilder,
    176                 Environment.NewLine
    177             });
    178             return new InvalidOperationException(message);
    179         }
    180 
    181         /// <summary>Creates the specified controller by using the specified request context.</summary>
    182         /// <returns>The controller.</returns>
    183         /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
    184         /// <param name="controllerName">The name of the controller.</param>
    185         /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>
    186         /// <exception cref="T:System.ArgumentException">The <paramref name="controllerName" /> parameter is null or empty.</exception>
    187         public virtual IController CreateController(RequestContext requestContext, string controllerName)
    188         {
    189             if (requestContext == null)
    190             {
    191                 throw new ArgumentNullException("requestContext");
    192             }
    193             if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())
    194             {
    195                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
    196             }
    197             Type controllerType = this.GetControllerType(requestContext, controllerName);
    198             return this.GetControllerInstance(requestContext, controllerType);
    199         }
    200 
    201         /// <summary>Retrieves the controller instance for the specified request context and controller type.</summary>
    202         /// <returns>The controller instance.</returns>
    203         /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
    204         /// <param name="controllerType">The type of the controller.</param>
    205         /// <exception cref="T:System.Web.HttpException">
    206         ///   <paramref name="controllerType" /> is null.</exception>
    207         /// <exception cref="T:System.ArgumentException">
    208         ///   <paramref name="controllerType" /> cannot be assigned.</exception>
    209         /// <exception cref="T:System.InvalidOperationException">An instance of <paramref name="controllerType" /> cannot be created.</exception>
    210         protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    211         {
    212             if (controllerType == null)
    213             {
    214                 throw new HttpException(404, string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound, new object[]
    215                 {
    216                     requestContext.HttpContext.Request.Path
    217                 }));
    218             }
    219             if (!typeof(IController).IsAssignableFrom(controllerType))
    220             {
    221                 throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase, new object[]
    222                 {
    223                     controllerType
    224                 }), "controllerType");
    225             }
    226             return this.ControllerActivator.Create(requestContext, controllerType);
    227         }
    228 
    229         /// <summary>Returns the controller's session behavior.</summary>
    230         /// <returns>The controller's session behavior.</returns>
    231         /// <param name="requestContext">The request context.</param>
    232         /// <param name="controllerType">The type of the controller.</param>
    233         protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
    234         {
    235             if (controllerType == null)
    236             {
    237                 return SessionStateBehavior.Default;
    238             }
    239             return DefaultControllerFactory._sessionStateCache.GetOrAdd(controllerType, delegate(Type type)
    240             {
    241                 SessionStateAttribute sessionStateAttribute = type.GetCustomAttributes(typeof(SessionStateAttribute), true).OfType<SessionStateAttribute>().FirstOrDefault<SessionStateAttribute>();
    242                 if (sessionStateAttribute == null)
    243                 {
    244                     return SessionStateBehavior.Default;
    245                 }
    246                 return sessionStateAttribute.Behavior;
    247             });
    248         }
    249 
    250         /// <summary>Retrieves the controller type for the specified name and request context.</summary>
    251         /// <returns>The controller type.</returns>
    252         /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
    253         /// <param name="controllerName">The name of the controller.</param>
    254         protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
    255         {
    256             if (requestContext == null)
    257             {
    258                 throw new ArgumentNullException("requestContext");
    259             }
    260             if (string.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch()))
    261             {
    262                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
    263             }
    264             RouteData routeData = requestContext.RouteData;
    265             if (routeData != null && routeData.HasDirectRouteMatch())
    266             {
    267                 return DefaultControllerFactory.GetControllerTypeFromDirectRoute(routeData);
    268             }
    269             object obj;
    270             if (routeData.DataTokens.TryGetValue("Namespaces", out obj))
    271             {
    272                 IEnumerable<string> enumerable = obj as IEnumerable<string>;
    273                 if (enumerable != null && enumerable.Any<string>())
    274                 {
    275                     HashSet<string> namespaces = new HashSet<string>(enumerable, StringComparer.OrdinalIgnoreCase);
    276                     Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces);
    277                     if (controllerTypeWithinNamespaces != null || false.Equals(routeData.DataTokens["UseNamespaceFallback"]))
    278                     {
    279                         return controllerTypeWithinNamespaces;
    280                     }
    281                 }
    282             }
    283             if (this.ControllerBuilder.DefaultNamespaces.Count > 0)
    284             {
    285                 HashSet<string> namespaces2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
    286                 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces2);
    287                 if (controllerTypeWithinNamespaces != null)
    288                 {
    289                     return controllerTypeWithinNamespaces;
    290                 }
    291             }
    292             return this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null);
    293         }
    294 
    295         private static Type GetControllerTypeFromDirectRoute(RouteData routeData)
    296         {
    297             IEnumerable<RouteData> directRouteMatches = routeData.GetDirectRouteMatches();
    298             List<Type> list = new List<Type>();
    299             foreach (RouteData current in directRouteMatches)
    300             {
    301                 if (current != null)
    302                 {
    303                     Type targetControllerType = current.GetTargetControllerType();
    304                     if (targetControllerType == null)
    305                     {
    306                         throw new InvalidOperationException(MvcResources.DirectRoute_MissingControllerType);
    307                     }
    308                     if (!list.Contains(targetControllerType))
    309                     {
    310                         list.Add(targetControllerType);
    311                     }
    312                 }
    313             }
    314             if (list.Count == 0)
    315             {
    316                 return null;
    317             }
    318             if (list.Count == 1)
    319             {
    320                 return list[0];
    321             }
    322             throw DefaultControllerFactory.CreateDirectRouteAmbiguousControllerException(list);
    323         }
    324 
    325         private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces)
    326         {
    327             this.ControllerTypeCache.EnsureInitialized(this.BuildManager);
    328             ICollection<Type> controllerTypes = this.ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
    329             switch (controllerTypes.Count)
    330             {
    331             case 0:
    332                 return null;
    333             case 1:
    334                 return controllerTypes.First<Type>();
    335             default:
    336                 throw DefaultControllerFactory.CreateAmbiguousControllerException(route, controllerName, controllerTypes);
    337             }
    338         }
    339 
    340         /// <summary>Releases the specified controller.</summary>
    341         /// <param name="controller">The controller to release.</param>
    342         public virtual void ReleaseController(IController controller)
    343         {
    344             IDisposable disposable = controller as IDisposable;
    345             if (disposable != null)
    346             {
    347                 disposable.Dispose();
    348             }
    349         }
    350 
    351         internal IReadOnlyList<Type> GetControllerTypes()
    352         {
    353             this.ControllerTypeCache.EnsureInitialized(this.BuildManager);
    354             return this.ControllerTypeCache.GetControllerTypes();
    355         }
    356 
    357         /// <summary>This API supports the ASP.NET MVC infrastructure and is not intended to be used directly from your code. This method calls the <see cref="M:System.Web.Mvc.DefaultControllerFactory.GetControllerSessionBehavior(System.Web.Routing.RequestContext,System.Type)" /> method.</summary>
    358         /// <returns>The controller's session behavior.</returns>
    359         /// <param name="requestContext">The request context.</param>
    360         /// <param name="controllerName">The controller name.</param>
    361         SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
    362         {
    363             if (requestContext == null)
    364             {
    365                 throw new ArgumentNullException("requestContext");
    366             }
    367             if (string.IsNullOrEmpty(controllerName))
    368             {
    369                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
    370             }
    371             Type controllerType = this.GetControllerType(requestContext, controllerName);
    372             return this.GetControllerSessionBehavior(requestContext, controllerType);
    373         }
    374     }


       既然是ControllerFactory,DefaultControllerFactory肯定也实现了IControllerFactory接口,别的我们就不看了,我们看看是如何创建Controller对象的,方法代码如下:

     1 /// <summary>Creates the specified controller by using the specified request context.</summary>
     2 /// <returns>The controller.</returns>
     3 /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
     4 /// <param name="controllerName">The name of the controller.</param>
     5 /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>
     6 /// <exception cref="T:System.ArgumentException">The <paramref name="controllerName" /> parameter is null or empty.</exception>
     7 public virtual IController CreateController(RequestContext requestContext, string controllerName)
     8 {
     9     if (requestContext == null)
    10     {
    11         throw new ArgumentNullException("requestContext");
    12     }
    13     if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())
    14     {
    15         throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
    16     }
    17     Type controllerType = this.GetControllerType(requestContext, controllerName);
    18     return this.GetControllerInstance(requestContext, controllerType);
    19 }


      代码很简单,该方法获取Controller的Type类型对象,然后根据Type对象创建实例。这个方法里面有两个辅助方法,一个是GetControllerType方法,另一个是GetControllerInstance方法,根据名称我们就能知道是做什么的。我们先看看GetControllerType方法的源码吧,这里是关键,没有Type对象的获取,以后都是空言:  

     1 /// <summary>Retrieves the controller type for the specified name and request context.</summary>
     2 /// <returns>The controller type.</returns>
     3 /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
     4 /// <param name="controllerName">The name of the controller.</param>
     5 protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
     6 {
     7     if (requestContext == null)
     8     {
     9         throw new ArgumentNullException("requestContext");
    10     }
    11     if (string.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch()))
    12     {
    13         throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
    14     }
    15     RouteData routeData = requestContext.RouteData;
    16     if (routeData != null && routeData.HasDirectRouteMatch())
    17     {
    18         return DefaultControllerFactory.GetControllerTypeFromDirectRoute(routeData);
    19     }
    20     object obj;
    21     if (routeData.DataTokens.TryGetValue("Namespaces", out obj))
    22     {
    23         IEnumerable<string> enumerable = obj as IEnumerable<string>;
    24         if (enumerable != null && enumerable.Any<string>())
    25         {
    26             HashSet<string> namespaces = new HashSet<string>(enumerable, StringComparer.OrdinalIgnoreCase);
    27             Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces);
    28             if (controllerTypeWithinNamespaces != null || false.Equals(routeData.DataTokens["UseNamespaceFallback"]))
    29             {
    30                 return controllerTypeWithinNamespaces;
    31             }
    32         }
    33     }
    34     if (this.ControllerBuilder.DefaultNamespaces.Count > 0)
    35     {
    36         HashSet<string> namespaces2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
    37         Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces2);
    38         if (controllerTypeWithinNamespaces != null)
    39         {
    40             return controllerTypeWithinNamespaces;
    41         }
    42     }
    43     return this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null);
    44 }

        我们先根据RouteData路由数据来获取Controller的类型对象,如果RouteData不为空,并且在RouteData的Values属性中包含Key为“MS_DirectRouteMatches”的值,那我们就据此获取Controller的类型对象,如果没找到就返回Null值,如果有一个值,就会作为Controller的Type类型值返回,如果多于一个就会抛出异常。如果RouteData不包含Key为“MS_DirectRouteMatches”的值,我们就根据RouteData对象中DataTokens属性Key为“Namespaces”来获取Controller的Type对象,同理,如果没找到就返回null,找到一个就直接返回,如果多余一个的话就抛出异常。

          如果我们还是没找到怎么办呢?我们就要看看能不能使用后备命名空间,如果可以,就根据此命名空间来查找,我们从RouteData路由数据的DataTokens属性中查找是否包含有Key为“UseNamespaceFallback”的值,如果有,并且是False,就直接返回,结束查找,如果不包含Key为“UseNamepsaceFallback”的值或者该值为True,我们就可以根据ControllerBuilder的DefaultNamespaces属性表示后备命名空间查找Controller的类型,同理,没有找到就返回null,找到一个就作为结果值直接返回,如果多于一个那就要抛出异常了。

        好了,大概的逻辑写完了,如果还有不明白的,就仔细的看看源码把,如果把源码完整的看一遍,然后在整理逻辑,就容易多了。看代码别太注意代码细节,关注主要的关键点或者叫关键类就可以,把哥哥类之间的关系和责任整理清楚,理解起来并不复杂。

    三、扩展点

          到了现在,Controller激活系统就写的差不多了,唯一还差一点的就是扩展点还没提。ASP.NET MVC号称几乎任何地方都可以扩展,Controller激活系统中肯定也包含着扩展点,下来我们一一详述。

          扩展点一:扩展IControllerFactory接口

                  第一个很容易想到的扩展点就是实现IControllerFactory接口,定义一个全新的ControllerFactory类型。如果是这样,里面所有的功能的就需要全部重新实现,包括Controller类型的解析、Controller实例的创建和销毁、也包括会话状态行为模式的管理。这样的好处就是我们就有更好的控制权了,可以按着我们的思想实现其功能。但是如果觉得重新实现有点麻烦,而且有时候也没有必要,拿我们就可以继承DefaultControllerFactory类型,针对特定的方法去扩展,实现我们自己的功能要求。比如我们想增加Ioc,有了IOC可以解耦Controller和Model的关系。一般来说,Controller实例的创建才需要IOC容器的控制,我们直接从DefaultControllerFactory类型继承,重写Controller实例创建的逻辑即可,这样也避免了全部重新实现,做起事来就更事半功倍了。

                 DefaultControllerFactory类型在创建Controller实例的时候,有两个方法是可以扩展的,一个是受保护的虚方法GetControllerType,我们可以定义自己的Controller类型的解析方法的实现,方法签名如下:

         protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)

                 其实这也是一个扩展点,但是我们今天不扩展这里,因为IOC框架和这里的关系不大,我们要扩展的是第二受保护的虚方法,方法签名如下:

         protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)

               这个方法是用于创建Controller实例的,这里正好可以使用IOC容器来管理Controller实例,当然在DefaultControllerFactory类型中还有其他的扩展点,大家可以自己去看,全部代码我以前也贴出来过。

     1     public class UnityControllerFactory:DefaultControllerFactory
     2     {
     3         /// <summary>
     4         /// IOC的容器对象
     5         /// </summary>
     6         public IUnityContainer UnityContainer { get; private set; }
     7 
     8         /// <summary>
     9         /// 通过构造函数初始化IOC的容器对象
    10         /// </summary>
    11         /// <param name="unityContainer">传入的IOC容器对象</param>
    12         public UnityControllerFactory(IUnityContainer unityContainer)
    13         {
    14             if (unityContainer != null)
    15             {
    16                 this.UnityContainer = unityContainer;
    17             }
    18             else
    19             {
    20                 throw new ArgumentNullException("容器对象不能为空!");
    21             }
    22         }
    23 
    24         /// <summary>
    25         /// 
    26         /// </summary>
    27         /// <param name="requestContext"></param>
    28         /// <param name="controllerType"></param>
    29         /// <returns></returns>
    30         protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    31         {
    32             if (controllerType == null)
    33             {
    34                 return null;
    35             }
    36             return (IController)this.UnityContainer.Resolve(controllerType);
    37         }
    38     }


              代码很简单,我们将解析出来的Controller类型作为参数调用UnityContainer对象的Resolve方法,进而得到最终激活的Controller对象。这是样例代码,具体项目中,还要引入Unity的DLL,还需要注册,这些我就不细说了。

         扩展点二:扩展ControllerActivator

               DefaultControllerFactory对象并不直接激活目标Controller对象,真正激活Controller对象的是一个叫做ControllerActiviator类型的对象,换句话说,DefaultControllerFactory对象委托ControllerActivator对象根据解析出来类型创建对应Controller对象。所有的ControllerActivator对象均实现IControllerActivator接口,该接口定义如下:

    /// <summary>Provides fine-grained control over how controllers are instantiated using dependency injection.</summary>
    public interface IControllerActivator
    {
        /// <summary>When implemented in a class, creates a controller.</summary>
        /// <returns>The created controller.</returns>
        /// <param name="requestContext">The request context.</param>
        /// <param name="controllerType">The controller type.</param>
        IController Create(RequestContext requestContext, Type controllerType);
    }

       该接口中定义了Create方法实现针对目标Controller对象的创建,两个参数分别表示请求上下文和Controller的类型。我们在看看DefaultControllerFactory和ControllerActivator之间的关系吧,DefaultControllerFactory类型有三个构造函数,代码如下:

     1 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class.</summary>
     2 public DefaultControllerFactory() : this(null, null, null)
     3 {
     4 }
     5 
     6 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class using a controller activator.</summary>
     7 /// <param name="controllerActivator">An object that implements the controller activator interface.</param>
     8 public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null)
     9 {
    10 }
    11 
    12 internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
    13 {
    14     if (controllerActivator != null)
    15     {
    16         this._controllerActivator = controllerActivator;
    17         return;
    18     }
    19     IResolver<IControllerActivator> arg_44_1 = activatorResolver;
    20     if (activatorResolver == null)
    21     {
    22         arg_44_1 = new SingleServiceResolver<IControllerActivator>(() => null, new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory constructor");
    23     }
    24     this._activatorResolver = arg_44_1;
    25 }


        第二个和第三个构造函数都有一个IControllerActivator类型的参数,第三个是程序集内部的,暂时不说,就说第二吧,DefaultControllerFactory正式通过该参数来创建目标Controller对象的,我们基于ControllerActivator对象来创建一个DefaultControllerFactory对象,他会最终被用于创建目标的Controller对象,我们的扩展点也就找到了。

         我们扩展的源码如下:

     1    public class NinjectControllerActivator : IControllerActivator
     2     {
     3         public IKernel Kernel { get; private set; }
     4 
     5         public NinjectControllerActivator()
     6         {
     7             this.Kernel = new StandardKernel();
     8         }
     9         public IController Create(RequestContext requestContext, Type controllerType)
    10         {
    11             return (IController)this.Kernel.TryGet(controllerType);
    12         }
    13 
    14         public void Register<TFrom, TTo>() where TTo : TFrom
    15         {
    16             this.Kernel.Bind<TFrom>().To<TTo>();
    17         }
    18     }

         扩展写好了,还需要在Global.asax文件里面进行注册,否则不能使用。IOC相关的内容就不介绍了,大家可以自行研究。

         扩展点三:扩展DependencyResolver

               如果我们在构建DefaultControllerFactory对象的时候没有显示指定采用的ControllerActiviator参数,则它会使用默认的一个ControllerActivator对象DefaultControllerActivator来完成,该类型是一个私有类型,外界无法访问。源码如下:

     1 private class DefaultControllerActivator : IControllerActivator
     2 {
     3     private Func<IDependencyResolver> _resolverThunk;
     4 
     5     public DefaultControllerActivator() : this(null)
     6     {
     7     }
     8 
     9     public DefaultControllerActivator(IDependencyResolver resolver)
    10     {
    11         if (resolver == null)
    12         {
    13             this._resolverThunk = (() => DependencyResolver.Current);
    14             return;
    15         }
    16         this._resolverThunk = (() => resolver);
    17     }
    18 
    19     public IController Create(RequestContext requestContext, Type controllerType)
    20     {
    21         IController result;
    22         try
    23         {
    24             result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
    25         }
    26         catch (Exception innerException)
    27         {
    28             throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[]
    29             {
    30                 controllerType
    31             }), innerException);
    32         }
    33         return result;
    34     }
    35 }

              其实DefaultControllerActivator对象要像创建目标Controller对象,还依赖另外一个叫DependencyResolver的对象。所有的DenpendencyResolver类型均实现了IDenpendencyResolver接口,接口定义如下:

     1 /// <summary>Defines the methods that simplify service location and dependency resolution.</summary>
     2 public interface IDependencyResolver
     3 {
     4     /// <summary>Resolves singly registered services that support arbitrary object creation.</summary>
     5     /// <returns>The requested service or object.</returns>
     6     /// <param name="serviceType">The type of the requested service or object.</param>
     7     object GetService(Type serviceType);
     8 
     9     /// <summary>Resolves multiply registered services.</summary>
    10     /// <returns>The requested services.</returns>
    11     /// <param name="serviceType">The type of the requested services.</param>
    12     IEnumerable<object> GetServices(Type serviceType);
    13 }

             系统中使用的DependencyResolver是通过DependencyResolver类型注册的。DependencyResolver类型有一个叫做Current的静态属性,该属性表示的是当前的、IDependencyResolver类型的对象。我们可以通过静态方法SetResolver来注册IDependencyResolver对象。需要说明一点,还有静态方法的DependencyResolver类型并没有实现IDependencyResolver接口,所以说该类型并不是真正的DependencyResolver类型,我们可以把它理解为一个Facade,它汇集了所要使用的对象和方法而已,使用更方便。

         如果我们没有对DependencyResolver进行显示的注册,则系统会使用一个默认的叫做DefaultDependencyResolver的对象,本对象也是一个私有类型,外界无法使用。

         当我们通过无参的构造函数或者通过将ControllerActivator参数指定为null的构造函数来创建DefaultControllerFactory实例的时候,那么用于激活Controller实例的将是通过DependencyResolver类型的静态属性Current表示的DependencyResolver对象来激活的,我们的扩展终于也找到了。

        最后实现的代码如下:

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


        好了,扩展点写完 ,大家有时间可以看看源码,可以有很多启发,获得的也更多。

    四、小结

        我们可以小结了,到此,Controler激活系统这个小节我就写完了。其实不是很复杂,大家在看的时候,刚开始别太关注代码的细节,先把握整个数据流向,或者叫请求脉络,把里面所涉及到的对象和关系都整理清楚了,然后再有针对性的去看涉及到的每个对象,不是画画图,不是画画,是画各个对象之间的关系图,图能画出来,说明你的心中就有了整体把握了。如果太关注细节,太关注某个类型的代码,也会很容易顾此失彼,因为我开始就是那样,总是看了后面忘记前面,请求的线路也不是很清楚。所以我们学习的时候也要有方法,方法对,学的就快,成就感就能很快产生。好了,说了这么多了,继续努力吧,希望我写的东西对大家有所帮助。

  • 相关阅读:
    ArcGIS Engine开发之地图浏览
    ArcGIS Engine开发之地图文档保存
    ArcGIS Engine开发之地图基本操作(4)
    ArcGIS Engine开发之地图基本操作(3)
    ArcGIS Engine开发之地图基本操作(2)
    ArcGIS Engine开发之地图基本操作(1)
    ArcGIS Engine开发前基础知识(4)
    C#读取Excel的三种方式以及比较
    XtraGrid RepositoryItemCheckEdit 显示状态以及单选多选问题
    Dev GridControl数据修改后实时更新数据源
  • 原文地址:https://www.cnblogs.com/PatrickLiu/p/7374104.html
Copyright © 2020-2023  润新知