本些列文章是以asp.net mvc源代碼為例按照asp.net mvc執行順序一一分析和解釋。上篇文章asp.net mvc源碼分析-Controllerl篇 如何創建Controller實例 講到了如何創建Controller,在創建后就調用 controller.Execute(RequestContext);
在ControllerBase的Execute方法很簡單
VerifyExecuteCalledOnce(); // 確保一個controller實例只調用一次,
Initialize(requestContext);//初始化 ControllerContext = new ControllerContext(requestContext, this);
using (ScopeStorage.CreateTransientScope()) {
ExecuteCore();//這個才是真正的執行
}
本系列文章主要是分析源代碼,分析里面的邏輯和實現細節,所以我們還是來看看VerifyExecuteCalledOnce這個方法吧。
internal void VerifyExecuteCalledOnce() {
if (!_executeWasCalledGate.TryEnter()) {
string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBase_CannotHandleMultipleRequests, GetType());
throw new InvalidOperationException(message);
}
}
internal sealed class SingleEntryGate {
private const int NOT_ENTERED = 0;
private const int ENTERED = 1;
private int _status;
// returns true if this is the first call to TryEnter(), false otherwise
public bool TryEnter() {
int oldStatus = Interlocked.Exchange(ref _status, ENTERED);
return (oldStatus == NOT_ENTERED);
}
}
當大家 看了TryEnter方法以后是不是覺得他們實現的很巧妙啊。保證一個類的一個實例方法只執行一次的一種實現方式。
而ExecuteCore這個方法在抽象類Controller中實現,Controller是ControllerBase的子類,
protected override void ExecuteCore() {
PossiblyLoadTempData();
try {
string actionName = RouteData.GetRequiredString("action");
if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) {
HandleUnknownAction(actionName);
}
}
finally {
PossiblySaveTempData();
}
}
其中 ActionInvoker.InvokeAction(ControllerContext, actionName)是真正的調用Action,我們放到后面來講,這節我們來看看PossiblyLoadTempData、PossiblySaveTempData這個2個方法。在每次action調用前加載,調用后保存。
internal void PossiblyLoadTempData() {
if (!ControllerContext.IsChildAction) {
TempData.Load(ControllerContext, TempDataProvider);
}
}
internal void PossiblySaveTempData() {
if (!ControllerContext.IsChildAction) {
TempData.Save(ControllerContext, TempDataProvider);
}
}
這 2個方法實現是不特別簡單啊,那么TempData屬性實現是否簡單了?
public TempDataDictionary TempData {
get {
if (ControllerContext != null && ControllerContext.IsChildAction) {
return ControllerContext.ParentActionViewContext.TempData;
}
if (_tempDataDictionary == null) {
_tempDataDictionary = new TempDataDictionary();
}
return _tempDataDictionary;
}
set {
_tempDataDictionary = value;
}
}
這里 需要注意一下的是如果當前Action是一個子Action則返回父輩Action的Controller的TempData。
一提到 TempData ,我們還知道ViewData、ViewBag也是保存數據的,它們之間有何區別了?
TempData 是TempDataDictionary類的實例 public class TempDataDictionary : IDictionary<string, object>
ViewData是ViewDataDictionary類的實例 public class ViewDataDictionary : IDictionary<string, object>
ViewBag是DynamicViewDataDictionary類的實例 internal sealed class DynamicViewDataDictionary : DynamicObject
一般 對它們的區別網上都是如下的內容:
get {
if (_tempDataProvider == null) {
_tempDataProvider = CreateTempDataProvider();
}
return _tempDataProvider;
}
set {
_tempDataProvider = value;
}
}
protected virtual ITempDataProvider CreateTempDataProvider() {
return new SessionStateTempDataProvider();
}
public class SessionStateTempDataProvider : ITempDataProvider { internal const string TempDataSessionStateKey = "__ControllerTempData"; public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext) { HttpSessionStateBase session = controllerContext.HttpContext.Session; if (session != null) { Dictionary<string, object> tempDataDictionary = session[TempDataSessionStateKey] as Dictionary<string, object>; if (tempDataDictionary != null) { // If we got it from Session, remove it so that no other request gets it session.Remove(TempDataSessionStateKey); return tempDataDictionary; } } return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); } 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); if (session == null) { if (isDirty) { throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled); } } else { if (isDirty) { session[TempDataSessionStateKey] = values; } else { // Since the default implementation of Remove() (from SessionStateItemCollection) dirties the // collection, we shouldn't call it unless we really do need to remove the existing key. if (session[TempDataSessionStateKey] != null) { session.Remove(TempDataSessionStateKey); } } } } }
session.Remove(TempDataSessionStateKey);
}

get {
RouteData routeData = RouteData;
if (routeData == null) {
return false;
}
return routeData.DataTokens.ContainsKey(PARENT_ACTION_VIEWCONTEXT);
}
}
controllerName:Test
routeValues:null
textWriter:htmlHelper.ViewContext.Writer
RouteData routeData = new RouteData();
foreach (KeyValuePair<string, object> kvp in routeValues) {
routeData.Values.Add(kvp.Key, kvp.Value);
}
foreach (KeyValuePair<string, object> kvp in dataTokens) {
routeData.DataTokens.Add(kvp.Key, kvp.Value);
}
routeData.Route = route;
routeData.DataTokens[ControllerContext.PARENT_ACTION_VIEWCONTEXT] = parentViewContext;
return routeData;
}