白话ASP.NET MVC之二:Controller激活系统的概览
????? 前文简介:我们抽象类路由规则的对象,RouteBase是路由对象的抽象基类,ASP.NET 的路由系统中有唯一一个从RouteBase继承的路由对象,那就是Route类型了。我们注册了路由对象Route,UrlRoutingModule截获请求,把当前请求的Url地址和RouteTable路由表中注册的路由对象一个一个的比较,如果没有找到就返回Null,请求就到此终止了。如果有匹配的路由对象,选择第一个匹配的Route对象,并且根据该Route对象,生成了路由数据RouteData对象,本对象是为了封装成RequestContext对象,RequestContext对象封装了RouteData和HttpContext对象。 ????? 其实RequestContext对象也是作为参数使用的,我们根据Route对象获得了RouteData,该对象有一个RouteHandler属性,这个属性的值在ASP.NET MVC中是MvcRouteHandler。RouteHandler主要的作用是提供用于处理最终请求的HttpHandler对象,代码如下: IHttpHandler=RouteData.RouteHandler.GetHttpHandler(requestContext);
HttpContext.Remap(IHttpHandler);
??? 上文中我们获取到的RequestContext,将此作为参数调用RouteHandler的GetHttpHandler方法,我们获得了IHttpHandler对象,我们需要把请求交给这个HttpHandler来处理,通过HttpContext.Remap(Handler)实现请求的交接,这个Handler在ASP.NET MVC中就是MvcHandler,我们既然获得了HttpHandler对象,也实现了请求的交接,下一步该做什么呢? 一、概览 ???? 我们获得了用于处理请求HttpHandler对象,下一步最重要的任务是把Controller对象找到,RouteData对象的Values属性的Controller键和Action键的值只是一个字符串的结果,我们要通过这个名称找到具体的Controller类型实例,才能执行Action方法,给客户想要的应答。这篇文章就是Controller的激活方法的详解。 ????? ASP.NET MVC系统真的很庞大,也很复杂,如果我们一上来就瀑布式的说,就像流水账一样,估计也不太容易说明白。为了便于理解,我们人为的把ASP.NET MVC这么庞大的系统拆分成相互独立,又有一定联系的多个子系统,然后我们各个击破,理解起来也就简单了,最后在把各个部分整合在一起,理解就全面了。今天我们要说的这一部分暂时叫做Controller激活系统吧,激活系统有两个含义,一是我们要找到我们需要的Controller对象,并实例化;二是我们药缓存他,并要执行它。 ??? 我们先来看看MvcHandler的源码吧,有助于我们的理解,代码如下: 1 /// <summary>Selects the controller that will handle an HTTP request.</summary> 2 public class MvcHandler : IHttpAsyncHandler,IHttpHandler,IRequiresSessionState 3 { 4 private struct ProcessRequestState 5 { 6 internal IAsyncController AsyncController; 7 8 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 } ?? 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的激活解析过程。 ?? 写一写很不错,把自己的理解写出来,现在更清晰了,也希望高手来指正和赐教。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc – 强大类型的ASP.NET MVC与ADO.NET实体框架
- asp.net – 如何在网站项目中使用发布配置文件(.pubxml)时设
- ASP.Net MVC C#另一个viewmodel中的两个viewmodel – 如何在
- asp.net-mvc – asp.net MVC-3视频教程?
- 什么是<%=或<%:在ASP.NET中调用?
- asp.net – HttpContext.Current.Cache可用于所有会话
- asp.net – .NET异常页面非人类可读
- ASP.NET TreeView和选择所选节点
- asp.net – (.NET Core 1.0 \u0026\u0026 net46)
- asp.net-mvc – 用于子操作的ASP.NET MVC路由匹配