一、概述
在此系列開篇的時候介紹了MVC的生命周期 , 對於請求的處理,都是將相應的類的方法注冊到HttpApplication事件中,通過事件的依次執行從而完成對請求的處理。對於MVC來說,請求是先 經過路由系統,然后由一個MvcHandler來處理的,當請求到來時,執行此MvcHandler的ProcessRequest方法(因為已將 MvcHandler類的ProcessRequest方法注冊到HttpApplication的事件中,所以事件的執行就觸發了此方法)。詳細請看之前介紹MVC生命周期的兩篇博客。
下面我們就以MVC聲明周期為主線,來分析下MVC源碼
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState { protected virtual void ProcessRequest(HttpContext httpContext) { //使用HttpContextWrapper對HttpContext進行封裝,封裝的目的是為了解耦以獲得可測試性.然后從RequestContext.RouteData中提取Controller名稱. HttpContextBase httpContext2 = new HttpContextWrapper(httpContext); this.ProcessRequest(httpContext2); } protected internal virtual void ProcessRequest(HttpContextBase httpContext) { IController controller; IControllerFactory controllerFactory; this.ProcessRequestInit(httpContext, out controller, out controllerFactory);//獲取到Controler和ControllerFactory實例,並賦值給局部變量 try { //Action的調用、View的呈現 controller.Execute(this.RequestContext); } finally { //釋放當前Controler對象 controllerFactory.ReleaseController(controller); } } }
MVC中Action的調用,就是通過調用Contrller對象的Execute方法觸發執行的!這個Controller對象是Controller激活的產物,Controller激活請參考上一篇博客。
二、Action的調用
我們知道Action的執行就是調用通過Controller激活得到的Controller對象的Execute方法,這個Controller對象就是我們創建的Controller(例如:HomeController)類的實例,而我們創建的HomeController等控制器都繼承自Controller類、Controller抽象類繼承ControllerBase抽象類、ControllerBase抽象類實現了IController接口。繼承和實現關系為:
我們創建的控制器通過Controller的激活創建了實例,然后執行該實例的Execute方法,Execute方法定義在接口IController中,實現在類ControllerBase中,而該Excute方法內又調用ControllerBase類的抽象方法ExecuteCore,抽象方法ExecuteCore又在Controller類中實現。所以Action調用的流程為:先執行ControllerBase類的Execute方法,再執行Controller類的ExcuteCore方法。所以執行過程為:【ControllerBase類中的Execute方法】-->【Controller類中的ExecuteCore方法】

namespace System.Web.Mvc { public interface IController { void Execute(RequestContext requestContext); } }

namespace System.Web.Mvc { public abstract class ControllerBase : IController { //省略其他成員 protected virtual void Execute(RequestContext requestContext) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (requestContext.HttpContext == null) { throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext"); } VerifyExecuteCalledOnce(); Initialize(requestContext); using (ScopeStorage.CreateTransientScope()) { //執行ExecuteCore方法 ExecuteCore(); } } //該方法在派生類Controller類中實現 protected abstract void ExecuteCore(); } }

namespace System.Web.Mvc { public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer { //省略其他成員 //實現ControllerBase中的抽象方法 protected override void ExecuteCore() { // If code in this method needs to be updated, please also check the BeginExecuteCore() and // EndExecuteCore() methods of AsyncController to see if that code also must be updated. PossiblyLoadTempData(); try { //從路由中獲取Action的名字 string actionName = RouteData.GetRequiredString("action"); //根據Action名字和上下文對Action進行調用 if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName); } } finally { PossiblySaveTempData(); } } } }
由上述代碼可以看出Action的執行最終實現在Controller類的ExecuteCore方法中,而其中ActionInvoker就是實現Action調用的組件,執行ActionInvoker的InvokeAction方法實現對Action的調用。
整個執行過程的功能為:【檢查對請求只做一次處理】-->【封裝請求上下文】-->【獲取上一次沒有被使用的TempData】-->【過濾器、Action的執行】-->【View的呈現(下一節介紹)】-->【將沒有被使用的TempData放入Session中】
//整個流程 public abstract class ControllerBase : IController { protected virtual void Execute(RequestContext requestContext) { //檢查對請求只做一次處理 VerifyExecuteCalledOnce(); //封裝請求上下文(RequestContext對象是在路由系統中創建的。其中封裝了請求上下文和路由信息。) Initialize(requestContext); //定義用於包含臨時作用域存儲的類。 基於 CurrentScope 屬性中的作用域,返回用於存儲臨時作用域內的數據的字典。 //這個的作用暫時還沒有弄清楚,不過通過重寫Execute方法,在using塊內可以獲取ScopeStorage的屬性CurrentScope的三個鍵值對。 using (ScopeStorage.CreateTransientScope()) { //執行Controller類中的ExecuteCore方法 ExecuteCore(); } } } public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer { protected override void ExecuteCore() { //獲取上一次沒有被使用的TempData PossiblyLoadTempData(); try { //從路由數據中獲取請求的Action的名字(路由系統從請求地址中獲取) string actionName = RouteData.GetRequiredString("action"); //過濾器、Action的執行、View的呈現 if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName); } } finally { //將沒有被使用的TempData放入Session中 PossiblySaveTempData(); } } }
==從以上的執行過程中各代碼的功能可以看出ExecuteCore()方法是Action執行的主操作,而VerifyExecuteCalledOnce()、Initialize(requestContext)兩個方法是前戲了。我們就先來分析下這兩個前戲的方法,主操作ExecuteCore()方法留着最后,並對其內部操作再進行詳細分析。
1、VerifyExecuteCalledOnce()
此方法保證了一次Http請求只進行一次處理

public abstract class ControllerBase : IController { private readonly SingleEntryGate _executeWasCalledGate = new SingleEntryGate(); //RequestContext對象是在路由系統中創建的。其中封裝了請求上下文和路由信息。 protected virtual void Execute(RequestContext requestContext) { //判斷對當期的請求是否是第一次執行處理 VerifyExecuteCalledOnce(); //執行Controller類的Initialize方法。(Initialize方法在Controller類中被重寫) Initialize(requestContext); //定義用於包含臨時作用域存儲的類。 基於 CurrentScope 屬性中的作用域,返回用於存儲臨時作用域內的數據的字典。 //這個的作用暫時還沒有弄清楚,不過通過重寫Execute方法,在using塊內可以獲取ScopeStorage的屬性CurrentScope的三個鍵值對。 using (ScopeStorage.CreateTransientScope()) { //執行Controller類中的ExecuteCore方法 ExecuteCore(); } } internal void VerifyExecuteCalledOnce() { //如果是TryEnter方法是第一次執行_executeWasCalledGate.TryEnter()=true //否則_executeWasCalledGate.TryEnter()=false if (!_executeWasCalledGate.TryEnter()) { string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBase_CannotHandleMultipleRequests, GetType()); throw new InvalidOperationException(message); } } }

internal sealed class SingleEntryGate { private const int NotEntered = 0; private const int Entered = 1; private int _status; // returns true if this is the first call to TryEnter(), false otherwise public bool TryEnter() { //Interlocked靜態類定義在System.Threading命名空間中且Exchange方法是extern的(定義在別的文件中)! //方法的概述為:以原子操作的形式,將 32 位有符號整數設置為指定的值並返回原始值。原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束 //Interlocked.Exchange方法將Extered的值設置給_status,並返回值為第一個參數_status的原始值 int oldStatus = Interlocked.Exchange(ref _status, Entered); return (oldStatus == NotEntered); } }
==上述代碼,在ControllerBase類中私有只讀字段_executeWasCalledGate創建了一個SingleEntryGate對象,而VerifyExecuteCalledOnce方法的功能就是通過 這個對象的TryTryEnter方法來實現的!如果TryTryEnter方法返回值為:true,則表示是第一次執行;否則非第一次執行,那么就拋出非法操作異常了。從而保證了一次Http請求只進行一次處理。
而內部到底是如何實現的呢?我們就在來看看SingleEntryGate類,其中的TeyEnter方法中調用了Interlocked.Exchange(ref int1,int2)方法,此方法定義的System.Threading命名空間內,方法的功能為:將第二個參數的值賦值給第一個參數,並將第一個參數原來的值作為方法的返回值。
例如:
如果是第一次調用TryEnter方法【Entered=1,_status=0,NotEntered = 0】執行完Interlocked.Exchange方法后【Entered = 1,_status=1,NotEntered = 0,oldStatus=0】,此時oldStatus=NotEntered = 0,返回true
如果是第n次調用TryEnter方法,那么此時的變量值還是第一次執行完的狀態【Entered = 1,_status=1,NotEntered = 0】,而執行完Interlocked.Exchange方法后【Entered = 1,_status=1,NotEntered = 0,oldStatus=1】,此時NotEntered = 0,oldStatus=1,不相等,返回false
注意,在ControllerBase類中私有只讀字段_executeWasCalledGate是非靜態的字段,所以實現的功能是【檢查每一次請求只執行一次】,如果是靜態字段,那么就變成了程序只執行一次請求的處理。(這功能的實現值得收藏)
2、Initialize(requestContext)
將 requestContext 和 當前請求的控制對象 封裝到一個 ControllerContext對象中!其中requestContext是已封裝了請求上下文和當前請求的路由信息的一個上下文。

public abstract class ControllerBase : IController { public ControllerContext ControllerContext { get; set; } //RequestContext對象是在路由系統中創建的。其中封裝了請求上下文和路由信息。 protected virtual void Execute(RequestContext requestContext) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (requestContext.HttpContext == null) { throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext"); } //判斷是否為第一次執行 VerifyExecuteCalledOnce(); //執行Controller類的Initialize方法。(Initialize方法在Controller類中被重寫) Initialize(requestContext); //定義用於包含臨時作用域存儲的類。 基於 CurrentScope 屬性中的作用域,返回用於存儲臨時作用域內的數據的字典。 //這個的作用暫時還沒有弄清楚,不過通過重寫Execute方法,在using塊內可以獲取ScopeStorage的屬性CurrentScope的三個鍵值對。 using (ScopeStorage.CreateTransientScope()) { //執行Controller類中的ExecuteCore方法 ExecuteCore(); } } protected virtual void Initialize(RequestContext requestContext) { ControllerContext = new ControllerContext(requestContext, this); } }

public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer { public UrlHelper Url { get; set; } protected override void Initialize(RequestContext requestContext) { base.Initialize(requestContext); Url = new UrlHelper(requestContext); } }

public class ControllerContext { internal const string ParentActionViewContextToken = "ParentActionViewContext"; private HttpContextBase _httpContext; private RequestContext _requestContext; private RouteData _routeData; // parameterless constructor used for mocking public ControllerContext() { } protected ControllerContext(ControllerContext controllerContext) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } Controller = controllerContext.Controller; RequestContext = controllerContext.RequestContext; } public ControllerContext(HttpContextBase httpContext, RouteData routeData, ControllerBase controller) : this(new RequestContext(httpContext, routeData), controller) { } //【Action的執行】過程中,通過請求上下文和請求的當期控制器對象(如:HomeController)創建控制器上下文。 public ControllerContext(RequestContext requestContext, ControllerBase controller) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (controller == null) { throw new ArgumentNullException("controller"); } RequestContext = requestContext;//上下文參數設置給私有變量 Controller = controller; //控制器(如:HomeController)對象設置給屬性 } public virtual ControllerBase Controller { get; set; } public IDisplayMode DisplayMode { get { return DisplayModeProvider.GetDisplayMode(HttpContext); } set { DisplayModeProvider.SetDisplayMode(HttpContext, value); } } public virtual HttpContextBase HttpContext { get { if (_httpContext == null) { _httpContext = (_requestContext != null) ? _requestContext.HttpContext : new EmptyHttpContext(); } return _httpContext; } set { _httpContext = value; } } public virtual bool IsChildAction { get { RouteData routeData = RouteData; if (routeData == null) { return false; } return routeData.DataTokens.ContainsKey(ParentActionViewContextToken); } } public ViewContext ParentActionViewContext { get { return RouteData.DataTokens[ParentActionViewContextToken] as ViewContext; } } public RequestContext RequestContext { get { if (_requestContext == null) { // still need explicit calls to constructors since the property getters are virtual and might return null HttpContextBase httpContext = HttpContext ?? new EmptyHttpContext(); RouteData routeData = RouteData ?? new RouteData(); _requestContext = new RequestContext(httpContext, routeData); } return _requestContext; } set { _requestContext = value; } } public virtual RouteData RouteData { get { if (_routeData == null) { _routeData = (_requestContext != null) ? _requestContext.RouteData : new RouteData(); } return _routeData; } set { _routeData = value; } } private sealed class EmptyHttpContext : HttpContextBase { } }
==上述代碼,對於Initialize方法的執行,由於ControllerBase中的Initialize方法在派生類Controller類中被重寫,所以要執行Controller類中的Initialize方法。方法內首先調用了父類ControllerBase中的Initialize方法創建了一個控制器上下文對象,並賦值給一個公有屬性。之后又創建了UrlHelper對象也賦值給了一個公有屬性。這個控制器上下文對象包含了從請求到現在的所有有用的數據,所以在之后對請求處理的步驟中隨處可見!這個UrlHelper對象還沒用到,暫且不議。
3、ExecuteCore()
ControllerBase類中定義了抽象方法ExecuteCore,該方法被派生類Controller類中實現!所以,要執行Controller類中的ExecuteCore方法,如上所言,此方法是Action執行的主操作,其中先對上一次操作沒有被TempData做處理,然后執行過濾器和請求的Action,最后進行View的呈現(下一節再對View的呈現做分析,現在只需知道這個執行的流程即可)。下面,我們就來用分析所有過程的代碼!
public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer { protected override void ExecuteCore() { //獲取上次處理過程中沒有被使用的TempData PossiblyLoadTempData(); try { //從路由數據中獲取請求的Action的名字 string actionName = RouteData.GetRequiredString("action"); //ActionInvoker為一個AsyncControllerActionInvoker對象 if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName); } } finally { //將TempData保存到Session中。等待之后將Session的key【__ControllerTempData】發送到響應流中! PossiblySaveTempData(); } } }
3-1、PossiblyLoadTempData()
此方法創建保存TempData的集合並獲取上一次請求中沒有被使用TempData添加到之前創建的那個集合中!
在介紹這個方法之前,有必要先了解下MVC框架下TempData的機制:
客戶端向服務發請求時,程序在執行本次請求的Action前,會先創建一個用來保存TempData的集合A,然后根據key=__ControllerTempData去服務器Session中獲取值並轉換為Dictionary<string, object>類型,如果得到的值為null,表示三種情況(1、當前是客戶端第一次向服務端發送請求。2、上次請求中沒有定義TempData值。3、上次請求中的TempData被View中用完了。);如果得到的值不為null,則表示上一次對請求的處理時TempData沒有被使用完,此時獲取的值就是上次處理請求時沒有被使用的TempData集合,然后將這集合賦值給我們開始創建的用於保存TempData的集合A中,再將key=__ControllerTempData的Session移除;之后執行Action方法內的代碼時,將本次的請求的Action中定義的TempData[""]=XXX也添加到最開始創建的那個集合A中,執行完Action方法后,在View的呈現中,如果使用了TempData,就將這個TempData從集合A中移除,執行完View后,則將保存TempData的集合A當作value,key=__ControllerTempData保存到服務器Session中。最后將所有Session的keys發送到響應流中!
上述提到那個保存TempData的集合A實際上是TempDataDictionary類型中的一個變量,該變量是一個字典類對象(new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase))
TempData是ControllerBase類中的一個類型為TempDataDictionary的屬性,TempData屬性其實就是一個TempDataDictionary對象,該對象中有一個字典類型的私有字段保存着所有TempData的值!
我們在Action方法中使用的TempData["kk"]=XX,其實就是調用的TempDataDictionary對象的索引器,將該鍵值對添加到保存所有TempData的私有字典表中!
擴展:Session的機制,Session保存在服務器,但是請求的最后服務端會將Session的所有key發送到響應流中!當再次發來請求時,可以從請求上下文中獲取到所有的keys。

public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer { protected override void ExecuteCore() { //獲取上次處理過程中沒有被使用的TempData PossiblyLoadTempData(); try { //從路由數據中獲取請求的Action的名字 string actionName = RouteData.GetRequiredString("action"); //ActionInvoker為一個AsyncControllerActionInvoker對象 if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName); } } finally { //將TempData保存到Session中。等待之后將Session的key【__ControllerTempData】發送到響應流中! PossiblySaveTempData(); } } private ITempDataProvider _tempDataProvider; internal IDependencyResolver Resolver { get { return _resolver ?? DependencyResolver.CurrentCache; } set { _resolver = value; } } public ITempDataProvider TempDataProvider { get { if (_tempDataProvider == null) { _tempDataProvider = CreateTempDataProvider(); } return _tempDataProvider; } set { _tempDataProvider = value; } } protected virtual ITempDataProvider CreateTempDataProvider() { //Resolver是Controller的一個屬性,返回值為:DependencyResolver.CurrentCache //Resolver.GetService<ITempDataProvider>() 可以不用看,因為返回的是null。這個方法其實先根據key=ITempDataProvider從全局的緩存表中查找值,如果有值,則返回。如果沒有,則利用DefaultDependencyResolver對象通過反射 //根據類型ITempDataProvider創建對象。但是由於這里的ITempDataProvider是接口,而DefaultDependencyResolver對象反射創建對象時,如果是接口或抽象類均返回null。詳細請看DependencyResolver類! //所以這段代碼,返回值就是 new SessionStateTempDataProvider(); return Resolver.GetService<ITempDataProvider>() ?? new SessionStateTempDataProvider(); } internal void PossiblyLoadTempData() { //檢查當前請求的Action是否為子Action。 //即:在View中是否應用了@Html.Action(),如果在View中嵌套了一個@Html.Action(action2),則這個action2也是需要再被執行一次的!即使Action的View中嵌套了一個Action,整個過程還是被 // 認為是一次請求的,此處判斷條件的目的是防止被嵌套的Action再去執行。 if (!ControllerContext.IsChildAction) { //TempData是基類ControllerBase中的屬性 //此處是TempData屬性被第一次使用,得到TempDataDictionary對象后,再執行此對象的Load方法,這個方法就是去實現獲取上一次處理請求時沒使用的TempData功能的! //參數TempDataProvider是關鍵,它是Controller類的一個屬性(就是一個SessionStateTempDataProvider對象),正如參數名的翻譯這個參數就是負責去請求上下文中檢查並獲取值的! TempData.Load(ControllerContext, TempDataProvider); } } }

public abstract class ControllerBase : IController { private TempDataDictionary _tempDataDictionary; //此時的ControllerContext是已經封裝 請求上下文、路由、當情請求的控制器對象。(在執行ExecuteCore方法之前的Execute方法中設置的!) public ControllerContext ControllerContext { get; set; } //第一次使用TempData屬性時(即:PossiblyLoadTempData方法是第一次使用),會創建一個TempDataDictionary對象並賦值給變量_tempDataDictionary,屬性的返回值就是該_tempDataDictionary變量。 //以后再使用TempData屬性時,返回都是第一次創建的那個TempDataDictionary對象,所以我們在Action方法中使用TempData[""]時,就是調用TempDataDictionary對象的索引器,向 //保存TempData集合添加一個鍵值對。 public TempDataDictionary TempData { get { if (ControllerContext != null && ControllerContext.IsChildAction) { return ControllerContext.ParentActionViewContext.TempData; } if (_tempDataDictionary == null) { //實例化TempDataDictionary,在構造函數中會創建一個不區分key大小寫的字典表,並賦值給一個私有字段。 _tempDataDictionary = new TempDataDictionary(); } return _tempDataDictionary; } set { _tempDataDictionary = value; } } }

public class TempDataDictionary : IDictionary<string, object> { internal const string TempDataSerializationKey = "__tempData"; private Dictionary<string, object> _data; private HashSet<string> _initialKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private HashSet<string> _retainedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public TempDataDictionary() { //初始化key不區分大小寫的字典類 _data = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); } public ICollection<string> Keys { get { return _data.Keys; } } //TempData["MyKey"]=MyValue,就是調用此索引器 //使用TempData時候,也是用這個索引,之后介紹! public object this[string key] { get { object value; if (TryGetValue(key, out value)) { _initialKeys.Remove(key); return value; } return null; } set { _data[key] = value; _initialKeys.Add(key); } } public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider) { //從Session中獲取上一次請求中沒有被使用的TempData IDictionary<string, object> providerDictionary = tempDataProvider.LoadTempData(controllerContext); //如果從Session中獲取的值不為null,則將Session獲取的值賦值給_data(將上次沒使用的TempData再次添加到集合中)。 //這里的判斷providerDictionary != null實際上是判斷取值是否出錯。 //因為tempDataProvider.LoadTempData(controllerContext)方法從Session中獲取值時,如果有值,則返回一個其中有鍵值對IDictionary<string, object>對象,否則返回一個沒有值的IDictionary<string, object>對象 _data = (providerDictionary != null) ? new Dictionary<string, object>(providerDictionary, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); //並將TempData的所有key保存到_initialKeys中! _initialKeys = new HashSet<string>(_data.Keys, StringComparer.OrdinalIgnoreCase); //清空_retainedKeys中的值,所以默認情況下,_retainedKeys的值為空 _retainedKeys.Clear(); } }

public class SessionStateTempDataProvider : ITempDataProvider { internal const string TempDataSessionStateKey = "__ControllerTempData"; public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext) { HttpSessionStateBase session = controllerContext.HttpContext.Session; //這里不是判斷上下文中是否含有Session的key,應該是請求是否出現異常! if (session != null) { //根據key=__ControllerTempData去服務器的Session中獲取值 Dictionary<string, object> tempDataDictionary = session[TempDataSessionStateKey] as Dictionary<string, object>; //如果Session中保存key為__ControllerTempData的值不為空,表示:上次處理請求時存在么有被使用的TempData,那么就返回這個字典對象(之后再添加到保存TempData的集合中,以供本次請求使用)。 //並將服務器的key=__ControllerTempData的Session移除! if (tempDataDictionary != null) { session.Remove(TempDataSessionStateKey); return tempDataDictionary; } } return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); } }

public class DependencyResolver { //靜態字段,所以在程序執行前就已經執行了該類的構造函數。 private static DependencyResolver _instance = new DependencyResolver(); private IDependencyResolver _current; private CacheDependencyResolver _currentCache; internal static IDependencyResolver CurrentCache { get { return _instance.InnerCurrentCache; } } internal IDependencyResolver InnerCurrentCache { get { return _currentCache; } } //構造函數 public DependencyResolver() { InnerSetResolver(new DefaultDependencyResolver()); } //構造函數調用此方法 public void InnerSetResolver(IDependencyResolver resolver) { //參數resolver=new DefaultDependencyResolver() //DefaultDependencyResolver類的功能為:通過反射根據類型創建對象! _current = resolver; _currentCache = new CacheDependencyResolver(_current); } //因為DependencyResolver只在靜態字段中進行了實例化,所有這個CacheDependencyResolver也只實例化一次。所以,ConcurrentDictionary字典也只實例化一次,故,在一次請求中這個集合是一直存在的! private sealed class CacheDependencyResolver : IDependencyResolver { private readonly ConcurrentDictionary<Type, object> _cache = new ConcurrentDictionary<Type, object>(); private readonly ConcurrentDictionary<Type, IEnumerable<object>> _cacheMultiple = new ConcurrentDictionary<Type, IEnumerable<object>>(); private readonly Func<Type, object> _getServiceDelegate; private readonly Func<Type, IEnumerable<object>> _getServicesDelegate; private readonly IDependencyResolver _resolver; public CacheDependencyResolver(IDependencyResolver resolver) { _resolver = resolver; //這個委托就是DefaultDependencyResolver實例的GetService方法,利用類型創建實例。 //目的就是當集合中沒有類型對應的值(實例)時,就通過DefaultDependencyResolver的GetService方法去再去反射創建實例。 _getServiceDelegate = _resolver.GetService; _getServicesDelegate = _resolver.GetServices; } public object GetService(Type serviceType) { //先根據serviceType去集合的key中找,如果對應的key有值,則直接將值返回。如果沒有,則將serviceType作為參數執行_getServiceDelegate委托,然后將執行委托后的返回值(利用類型創建對象)返回,並將此serviceType和執行委托后的返回值添加到集合中! return _cache.GetOrAdd(serviceType, _getServiceDelegate); } public IEnumerable<object> GetServices(Type serviceType) { // Use a saved delegate to prevent per-call delegate allocation return _cacheMultiple.GetOrAdd(serviceType, _getServicesDelegate); } } private class DefaultDependencyResolver : IDependencyResolver { public object GetService(Type serviceType) { if (serviceType.IsInterface || serviceType.IsAbstract) { return null; } try { return Activator.CreateInstance(serviceType); } catch { return null; } } public IEnumerable<object> GetServices(Type serviceType) { return Enumerable.Empty<object>(); } private Func<Type, object> _getService; private Func<Type, IEnumerable<object>> _getServices; public DelegateBasedDependencyResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices) { _getService = getService; _getServices = getServices; } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This method might throw exceptions whose type we cannot strongly link against; namely, ActivationException from common service locator")] public object GetService(Type type) { try { return _getService.Invoke(type); } catch { return null; } } public IEnumerable<object> GetServices(Type type) { return _getServices(type); } } }
上述代碼,TempDataDictionary類就是程序中使用的TempData的類型,SessionStateTempDataProvider類用於服務器Session中獲取上一次沒有被使用的TempData的集合,DependencyResolver類在上一節的Controller激活中已經介紹過了,它主要是用於根據類型通過反射創建實例(還具有緩存的功能)。
補充:對於我們定義的TempData["kk"]=value,如果在使用時也是執行的TempDataDictionary類的索引器,在索引器的Set中可看到,如果使用TempData之后,是在【_initialKeys】中將key移除,而不是直接在保存所有TempData的集合【_data】中移除。
猜想:MVC3和MVC4中對TempData的使用不一樣
3-2、ActionInvoker.InvokeAction(ControllerContext, actionName)
此代碼實現了:Action的執行、過濾器的執行、View的呈現。
由於這部分的內容太多,為避免影響知識點混亂,將再開一篇博文來詳細介紹!《白話學習MVC(八)Action的執行2》
3-3、PossiblySaveTempData()
從保存着所有TempData的集合中移除已經被使用的TempData,最后再將所有沒有被使用TempData集合保存在key=__ControllerTempData的Session中!以便下次請求中使用。

public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer { protected override void ExecuteCore() { //獲取上次處理過程中沒有被使用的TempData PossiblyLoadTempData(); try { //從路由數據中獲取請求的Action的名字 string actionName = RouteData.GetRequiredString("action"); //ActionInvoker為一個AsyncControllerActionInvoker對象 if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName); } } finally { //將TempData保存到Session中。等待之后將Session的key【__ControllerTempData】發送到響應流中! PossiblySaveTempData(); } } private ITempDataProvider _tempDataProvider; internal IDependencyResolver Resolver { get { return _resolver ?? DependencyResolver.CurrentCache; } set { _resolver = value; } } public ITempDataProvider TempDataProvider { get { if (_tempDataProvider == null) { _tempDataProvider = CreateTempDataProvider(); } return _tempDataProvider; } set { _tempDataProvider = value; } } protected virtual ITempDataProvider CreateTempDataProvider() { //Resolver是Controller的一個屬性,返回值為:DependencyResolver.CurrentCache //Resolver.GetService<ITempDataProvider>() 可以不用看,因為返回的是null。這個方法其實先根據key=ITempDataProvider從全局的緩存表中查找值,如果有值,則返回。如果沒有,則利用DefaultDependencyResolver對象通過反射 //根據類型ITempDataProvider創建對象。但是由於這里的ITempDataProvider是接口,而DefaultDependencyResolver對象反射創建對象時,如果是接口或抽象類均返回null。詳細請看DependencyResolver類! //所以這段代碼,返回值就是 new SessionStateTempDataProvider(); return Resolver.GetService<ITempDataProvider>() ?? new SessionStateTempDataProvider(); } internal void PossiblySaveTempData() { if (!ControllerContext.IsChildAction) { //最后一次使用TempData,TempData屬性定義在基類ControllerBase中 TempData.Save(ControllerContext, TempDataProvider); } } }

public abstract class ControllerBase : IController { private TempDataDictionary _tempDataDictionary; //此時的ControllerContext是已經封裝 請求上下文、路由、當情請求的控制器對象。(在執行ExecuteCore方法之前的Execute方法中設置的!) public ControllerContext ControllerContext { get; set; } //第一次使用TempData屬性時(即:PossiblyLoadTempData方法是第一次使用),會創建一個TempDataDictionary對象並賦值給變量_tempDataDictionary,屬性的返回值就是該_tempDataDictionary變量。 //以后再使用TempData屬性時,返回都是第一次創建的那個TempDataDictionary對象,所以我們在Action方法中使用TempData[""]時,就是調用TempDataDictionary對象的索引器,向 //保存TempData集合添加一個鍵值對。 //此時不是第一次使用TempData,應該是最后一次了 public TempDataDictionary TempData { get { if (ControllerContext != null && ControllerContext.IsChildAction) { return ControllerContext.ParentActionViewContext.TempData; } if (_tempDataDictionary == null) { //實例化TempDataDictionary,在構造函數中會創建一個不區分key大小寫的字典表,並賦值給一個私有字段。 _tempDataDictionary = new TempDataDictionary(); } return _tempDataDictionary; } set { _tempDataDictionary = value; } } }

public class TempDataDictionary : IDictionary<string, object> { private Dictionary<string, object> _data; private HashSet<string> _initialKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private HashSet<string> _retainedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); //沒有執行Action之前,對TempData的操作 public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider) { //從Session中獲取上一次請求中沒有被使用的TempData IDictionary<string, object> providerDictionary = tempDataProvider.LoadTempData(controllerContext); //如果從Session中獲取的值不為null,則將Session獲取的值賦值給_data(將上次沒使用的TempData再次添加到集合中)。 //這里的判斷providerDictionary != null實際上是判斷取值是否出錯。 //因為tempDataProvider.LoadTempData(controllerContext)方法從Session中獲取值時,如果有值,則返回一個其中有鍵值對IDictionary<string, object>對象,否則返回一個沒有值的IDictionary<string, object>對象 _data = (providerDictionary != null) ? new Dictionary<string, object>(providerDictionary, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); //並將TempData的所有key保存到_initialKeys中! _initialKeys = new HashSet<string>(_data.Keys, StringComparer.OrdinalIgnoreCase); //清空_retainedKeys中的值 _retainedKeys.Clear(); } public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider) { //RemoveFromDictionary方法是MVC中為IDictionary<TKey, TValue>的創建的一個擴展方法。 //RemoveFromDictionary方法作用:檢查TempData是否被使用過,如果已被使用,則將其在保存所有TempData集合(_data)中移除。 //須知:當使用TempData時,只是在_initialKeys中將key移除,並沒有在保存所有TempData集合(_data)中移除(見TempDataDictionary類的索引器) _data.RemoveFromDictionary((KeyValuePair<string, object> entry, TempDataDictionary tempData) => { string key = entry.Key; //只有當前TempDataDictionary對象的_initialKeys和_retainedKeys中都不含有_data中的key時,委托才返回true。 //_initialKeys和_retainedKeys初始化都是在 Load 方法中,_initialKeys中的值為所有TempData的key,而_retainedKeys是為空的集合 //目前所知,因為retainedKeys是為空的集合,所以tempData._retainedKeys.Contains(key)為:false //而當使用TempData時,只是在_initialKeys中將key移除。所以,如果是被使用過的TempData,則tempData._initialKeys.Contains(key)為:false,否則都為true //最終得出:如果當前循環的TempData被使用了,則返回ture,否則返回false return !tempData._initialKeys.Contains(key)&& !tempData._retainedKeys.Contains(key); }, this); //將所有沒有被使用的TempData的集合添加到服務器Session中,以便下次請求再使用 tempDataProvider.SaveTempData(controllerContext, _data); } }

internal static class DictionaryExtensions { //此時的TState類型為:TempDataDictionary public static void RemoveFromDictionary<TKey, TValue, TState>(this IDictionary<TKey, TValue> dictionary, Func<KeyValuePair<TKey, TValue>, TState, bool> removeCondition, TState state) { Contract.Assert(dictionary != null); Contract.Assert(removeCondition != null); int removeCount = 0; TKey[] keys = new TKey[dictionary.Count]; //循環所有的TempData,讓每個循環項執行委托removeCondition foreach (var entry in dictionary) { //對於委托執行removeCondition如果當前循環的TempData被使用了,則返回ture,否則返回false if (removeCondition(entry, state)) { //將已經被使用的TempData的key添加到keys數組中 //removeCount++表示已經被使用的TempData個數。 keys[removeCount] = entry.Key; removeCount++; } } //循環被刪除的TempData個數,將該鍵值對在保存所有TempData的集合中移除。 for (int i = 0; i < removeCount; i++) { dictionary.Remove(keys[i]); } } }

public class SessionStateTempDataProvider : ITempDataProvider { internal const string TempDataSessionStateKey = "__ControllerTempData"; public virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } HttpSessionStateBase session = controllerContext.HttpContext.Session; bool isDirty = (values != null && values.Count > 0); //這里的判斷:不是沒有Sesson,因為即使是第一次請求到來時(無任何Session),這個對象也不為空。 //而是根據上下文由於創建HttpSessionStateBase對象時出問題,該對象為空。 if (session == null) { if (isDirty) { throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled); } } //HttpSessonStateBase對象創建沒問題 else { //如果保存TempData的字典類實例中有值(也就是定義的TempData沒有在view中被全使用),將值設置給key為__ControllerTempData的Session。 if (isDirty) { session[TempDataSessionStateKey] = values; } //TempData在view中全部已被使用 else { //並且Session中該可以__ControllerTempData還有對應的值 //如果用戶在Action中自己定義一個key為__ControllerTempData的Session,這里移除Session中的這個值,防止了將這個key發送到相應流,以偽造成未被利用的TempData if (session[TempDataSessionStateKey] != null) { session.Remove(TempDataSessionStateKey); } } } } }
上述代碼,PossiblySaveTempData方法執行 TempData.Save(ControllerContext, TempDataProvider)來完成所有的功能,TempData得到的之前創建的TempDataDictionary對象(該對象的_data字段保存這定義的所有TempData鍵值對),參數TempDataProvider是創建的SessionStateTempDataProvider對象(該對象的SaveTempData方法的作用就是將沒有被使用的TempData保存到Session中)。所以,TempDataDictionary對象的Save方法首先去遍歷所有的TempData,檢查TempData是否被使用過了,如果已被使用,則將該TempData在保存所有TempData的集合中移除,最后執行SessionStateTempDataProvider對象的SaveTempData方法將經過處理后的集合添加到Session中,以便這些TempData在寫一次請求中使用!
如3-1中補充到,對於我們定義的TempData["kk"]=value,如果在使用時是執行的TempDataDictionary類的索引器,在索引器的Set中可看到,如果使用TempData之后,是在【_initialKeys】中將key移除,而不是直接在保存所有TempData的集合【_data】中移除。此處做的就是在根據【_initialKeys】將被使用的TempData在【_data】中移除。
以上就是全部內容,如有不合適之處請指教!
遺留問題:
1、如何利用各擴展點暫沒有分析。
疑問:
1、既然利用DependencyResolver通過反射創建對象時,接口和抽象類都不可以,MVC中為什么還在使用GetService方法使用接口來創建對象呀?明知是Null
更正:
1、以上的Controller類的繼承關系是MVC5中的,在MVC4中應該是如下,MVC4中沒有引進IAuthenticationFilter過濾器。
public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer { ... }