加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

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

发布时间:2020-12-16 08:58:43 所属栏目:asp.Net 来源:网络整理
导读:????? 前文简介:我们抽象类路由规则的对象,RouteBase是路由对象的抽象基类,ASP.NET 的路由系统中有唯一一个从RouteBase继承的路由对象,那就是Route类型了。我们注册了路由对象Route,UrlRoutingModule截获请求,把当前请求的Url地址和RouteTable路由表中

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

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

 1          2          3          4  5  6  7              8              9 10                 controller.Execute(11 12             13 14 15 16 17 
18         19 20             HttpContext current =21             22 23 24 25             26             27             28             factory = 29             controller = factory.CreateController(30             31 32                 33 34 35 36 37 38         }

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

二、Controller对象激活详述

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

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

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

interface IController
{
    Executes the specified request context.</summary>
    </param>
     Execute(RequestContext requestContext);
}

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

 1  IAsyncController : IController
 2 {
 3      4     The status of the asynchronous operation. 5      6      7     The state. 8     IAsyncResult BeginExecute(RequestContext requestContext,1)"> state);
 9 
10     Ends the asynchronous operation.11     12      EndExecute(IAsyncResult asyncResult);
13 }

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

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

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

  1 Represents the base class for all MVC controllers.  2  ControllerBase : IController
  4     readonly SingleEntryGate _executeWasCalledGate =  SingleEntryGate();
  5 
  6      DynamicViewDataDictionary _dynamicViewDataDictionary;
  8      TempDataDictionary _tempDataDictionary;
 10     bool _validateRequest =  12      IValueProvider _valueProvider;
 13 
 14      ViewDataDictionary _viewDataDictionary;
 15 
 16     Gets or sets the controller context. 17     The controller context. 18      ControllerContext ControllerContext
 19  21          22     }
 23 
 24     Gets or sets the dictionary for temporary data. 25     The dictionary for temporary data. 26      TempDataDictionary TempData
 27  28          29  30             this.ControllerContext != null && .ControllerContext.IsChildAction)
 31  32                 .ControllerContext.ParentActionViewContext.TempData;
 33  34             this._tempDataDictionary ==  35  36                 this._tempDataDictionary =  TempDataDictionary();
 37  38             ._tempDataDictionary;
 39  40          42             this._tempDataDictionary = 43  44  45 
 46     Gets or sets a value that indicates whether request validation is enabled for this request. 47     true if request validation is enabled for this request; otherwise,false. The default is true. 48      ValidateRequest
 50          51  52             ._validateRequest;
 53  54          55  56             this._validateRequest = 57  59 
 60     Gets or sets the value provider for the controller. 61     The value provider for the controller. 62      IValueProvider ValueProvider
 63  64          65 this._valueProvider ==  68                 this._valueProvider = ValueProviderFactories.Factories.GetValueProvider(.ControllerContext);
 69  70             ._valueProvider;
 71  72          73  74             this._valueProvider = 75  77 
 78     Gets the dynamic view data dictionary. 79     The dynamic view data dictionary. 80     [Dynamic]
 81     dynamic ViewBag
 82  83         [: Dynamic]
 84          86             this._dynamicViewDataDictionary ==  88                 this._dynamicViewDataDictionary = new DynamicViewDataDictionary(() => .ViewData);
 90             ._dynamicViewDataDictionary;
 91  92  93 
 94     Gets or sets the dictionary for view data. 95     The dictionary for the view data. 96      ViewDataDictionary ViewData
 98         100             this._viewDataDictionary == 101 102                 this._viewDataDictionary =  ViewDtaDictionary();
103 104             ._viewDataDictionary;
105 108             this._viewDataDictionary =109 111     112     113     114      Execute(RequestContext requestContext)
115 116         117 118             120         if (requestContext.HttpContext == 121 122             new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext,123 124         .VerifyExecuteCalledOnce();
125         .Initialize(requestContext);
126         using (ScopeStorage.CreateTransientScope())
128             .ExecuteCore();
130 131 
132     Executes the request.133      ExecuteCore();
134 
135     Initializes the specified request context.136     137      Initialize(RequestContext requestContext)
139         this.ControllerContext = new ControllerContext(requestContext,1)">141 
142      VerifyExecuteCalledOnce()
144         ._executeWasCalledGate.TryEnter())
145 146             string message = 148                 base.GetType()
            });
150              InvalidOperationException(message);
152 153 
154     155     156      IController.Execute(RequestContext requestContext)
157 158         .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>
 Initialize(RequestContext requestContext)
{
    );
}

此方法是虚方法,我们可以扩展他,这个方法的调用点在受保护的虚方法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。

virtual IAsyncResult BeginExecute(RequestContext requestContext,1)"> 3             .DisableAsyncSupport)
 5                 Action action =  7                      8  9                 10 11             12 13                 15             16             17             BeginInvokeDelegate<Controller> beginDelegate = (AsyncCallback asyncCallback,1)">object callbackState,Controller controller) => controller.BeginExecuteCore(asyncCallback,callbackState);
18             EndInvokeVoidDelegate<Controller> endDelegate = 20                 controller.EndExecuteCore(asyncResult);
21 22             return AsyncResultWrapper.Begin<Controller>(callback,1)">this,Controller._executeTag,1)">1,1)">23 }

?

????? 2、ControllerContext

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

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. ControllerContext
sealed  EmptyHttpContext : HttpContextBase
  6 const string ParentActionViewContextToken = ParentActionViewContext HttpContextBase _httpContext;
 RequestContext _requestContext;
 RouteData _routeData;
Gets or sets the controller.The controller.virtual ControllerBase Controller
Gets the display mode.The display mode. IDisplayMode DisplayMode
return DisplayModeProvider.GetDisplayMode(.HttpContext);
 32          34             DisplayModeProvider.SetDisplayMode(.HttpContext,value);
 37 
 38     Gets or sets the HTTP context. 39      40      HttpContextBase HttpContext
 42          44             this._httpContext ==  45  46                 this._httpContext = ((this._requestContext != null) ? this._requestContext.HttpContext :  ControllerContext.EmptyHttpContext());
 47 ._httpContext;
this._httpContext = 55 
 56     Gets a value that indicates whether the associated action method is a child action. 57     true if the associated action method is a child action; otherwise,1)"> 58      IsChildAction
 60          61  62             RouteData routeData = .RouteData;
 63             return routeData != null && routeData.DataTokens.ContainsKey( 66 
 67     Gets an object that contains the view context information for the parent action method. 68     An object that contains the view context information for the parent action method. 69      ViewContext ParentActionViewContext
 70 this.RouteData.DataTokens["]  ViewContext;
 76 
 77     Gets or sets the request context. 79      81          83             this._requestContext ==  84  85                 HttpContextBase httpContext = this.HttpContext ??  ControllerContext.EmptyHttpContext();
 86                 RouteData routeData = this.RouteData ??  RouteData();
 87                 this._requestContext =  RequestContext(httpContext,routeData);
 88  89             ._requestContext;
 90  91          93             this._requestContext = 95  96 
 97     Gets or sets the URL route data. 98     The URL route data. 99      RouteData RouteData
100 101         102 103             this._routeData == 104 105                 this._routeData = ((this._requestContext.RouteData :  RouteData());
106 107             ._routeData;
108 109         111             this._routeData =112 113 114 
115     <see cref="T:System.Web.Mvc.ControllerContext" />116      ControllerContext()
119 
120      class by using the specified controller context.121     <param name="controllerContext">122     <paramref name="controllerContext" />123     protected ControllerContext(ControllerContext controllerContext)
if (controllerContext == 127             controllerContext128 129         this.Controller = controllerContext.Controller;
130          controllerContext.RequestContext;
132 
133      class by using the specified HTTP context,URL route data,and controller.134     <param name="routeData">The route data.<param name="controller">public ControllerContext(HttpContextBase httpContext,RouteData routeData,ControllerBase controller) : this(139 140 
141      class by using the specified request context and controller.142     143     144     One or both parameters are null.145      ControllerContext(RequestContext requestContext,ControllerBase controller)
147         149             150 151         153             154 155         156          controller;
158 }

?

????? 3、ControllerFactory

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

 1       Defines the methods that are required for a controller factory. 2      IControllerFactory
 3  4         Creates the specified controller by using the specified request context. 5          6          7         <param name="controllerName">The name of the controller. 8         IController CreateController(RequestContext requestContext,1)"> controllerName);
10         Gets the controller's session behavior.11         The controller's session behavior.12         13         The name of the controller whose session behavior you want to get.14         SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext,1)">15 
16         Releases the specified controller.17          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类型的定义吧,代码如下:

class MvcHandler : IHttpAsyncHandler,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      Represents a class that is responsible for dynamically building a controller. ControllerBuilder
static ControllerBuilder _instance =  ControllerBuilder();
  6         private Func<IControllerFactory> _factoryThunk = () =>   8         private HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
 10         private IResolver<IControllerFactory> _serviceResolver;
 12         Gets the current controller builder object. 13         The current controller builder. 14         static ControllerBuilder Current
 16              17  18                  ControllerBuilder._instance;
 20 Gets the default namespaces. 23         The default namespaces. 24         public HashSet< DefaultNamespaces
 25  26              28                 ._namespaces;
 31 
 32         <see cref="T:System.Web.Mvc.ControllerBuilder" /> 33         public ControllerBuilder() :  36 
 37         internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
 39             IResolver<IControllerFactory> arg_6A_1 = serviceResolver;
 40             if (serviceResolver ==  42                 arg_6A_1 = new SingleServiceResolver<IControllerFactory>(() => this._factoryThunk(),1)"> DefaultControllerFactory
 44                     ControllerBuilder = this
 45                 },1)">ControllerBuilder.GetControllerFactorythis._serviceResolver = arg_6A_1;
 48  49 
 50         Gets the associated controller factory.The controller factory. 52          IControllerFactory GetControllerFactory()
 54             ._serviceResolver.Current;
 56 
 57         Sets the specified controller factory. 58         <param name="controllerFactory"> 59         <paramref name="controllerFactory" /> SetControllerFactory(IControllerFactory controllerFactory)
 62             if (controllerFactory ==  64                 controllerFactorythis._factoryThunk = (() =>Sets the controller factory by using the specified type.<param name="controllerFactoryType">The type of the controller factory. 71         <paramref name="controllerFactoryType" /> 72         <exception cref="T:System.ArgumentException">The controller factory cannot be assigned from the type in the  parameter. 73         <exception cref="T:System.InvalidOperationException">An error occurred while the controller factory was being set. 74          SetControllerFactory(Type controllerFactoryType)
 76             if (controllerFactoryType ==  78                 controllerFactoryType 79  80             typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType))
 81  82                 new ArgumentException(                    controllerFactoryType
 85                 }),1)"> 86  87             this._factoryThunk =                 IControllerFactory result;
 90                  92                     result = (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
 93  94                 catch (Exception innerException)
 96                                             controllerFactoryType
                    }),innerException);
101                 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 Filesroot....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的释放很简单,直接上代码吧。

The controller to release. ReleaseController(IController controller)
{
    IDisposable disposable = controller  IDisposable;
    if (disposable != )
    {
        disposable.Dispose();
    }
}

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

四、小结

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

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读