本些列文章是以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
ViewData是ViewDataDictionary类的实例 public class ViewDataDictionary : IDictionary
ViewBag是DynamicViewDataDictionary类的实例 internal sealed class DynamicViewDataDictionary : DynamicObject
一般 对它们的区别网上都是如下的内容:
public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider) {
IDictionary providerDictionary = tempDataProvider.LoadTempData(controllerContext);
_data = (providerDictionary != null) ? new Dictionary(providerDictionary, StringComparer.OrdinalIgnoreCase) :
new Dictionary(StringComparer.OrdinalIgnoreCase);
_initialKeys = new HashSet(_data.Keys, StringComparer.OrdinalIgnoreCase);
_retainedKeys.Clear();
}
public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider) {
string[] keysToKeep = _initialKeys.Union(_retainedKeys, StringComparer.OrdinalIgnoreCase).ToArray();
string[] keysToRemove = _data.Keys.Except(keysToKeep, StringComparer.OrdinalIgnoreCase).ToArray();
foreach (string key in keysToRemove) {
_data.Remove(key);
}
tempDataProvider.SaveTempData(controllerContext, _data);
}
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 Add(string key, object value) {
_data.Add(key, value);
_initialKeys.Add(key);
}
仔细看以上代码,我们会发现Load只是初始化一个默认的字典,没什么特别的,
而Save就有所特别,它在每次保存的时候都移除此次添加的key,说白了又回到初始状态了。反正我是没明白微软为什么要这个做。不保存不就行了吗?
public class SessionStateTempDataProvider : ITempDataProvider {
internal const string TempDataSessionStateKey = "__ControllerTempData";
public virtual IDictionary LoadTempData(ControllerContext controllerContext) {
HttpSessionStateBase session = controllerContext.HttpContext.Session;
if (session != null) {
Dictionary tempDataDictionary = session[TempDataSessionStateKey] as Dictionary;
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(StringComparer.OrdinalIgnoreCase);
}
public virtual void SaveTempData(ControllerContext controllerContext, IDictionary 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);
}
}
}
}
}