首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > PowerDesigner >

asp.net mvc源码分析-Controllerl篇 TempData数据储存

2012-11-13 
asp.net mvc源码分析-Controllerl篇 TempData数据存储本些列文章是以asp.net mvc源代码为例按照asp.net mv

asp.net mvc源码分析-Controllerl篇 TempData数据存储

本些列文章是以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

一般 对它们的区别网上都是如下的内容:

TempData:保存在Session中,Controller每次执行请求的时候,会从Session中先获取TempData,而后清除Session,获取完TempData数据,虽然保存在内部字典对象中,但是其集合中的每个条目访问一次后就从字典表中删除。具体代码层面,TempData获取过程是通过SessionStateTempDataProvider.LoadTempData方法从ControllerContext的Session中读取数据,而后清除Session,故TempData只能跨Controller传递一次。ViewData:生命周期和View相同,仅对当前View有效。ViewBag:和ViewData生命周期相同,也是对但前View有效,不同的是ViewBag的类型不再是字典的键值对结构,而是dynamic动态类型,属于MVC3里面新增的部分。这里的TempData解释是对的吗?我们 这里主要讲讲TempData,其他2个很简单,TempDataDictionary类主要代码如下:

为什么了会这样了,原因很简单,虽然我们调用@{Html.RenderAction("Index","Test");}时候会去执行  PossiblyLoadTempData()、 PossiblySaveTempData()这2个方法,但是他们有一个过滤条件  if (!ControllerContext.IsChildAction) {} 这个条件不满足,所以实际上就没有调用TempData.Load和TempData.Save方法。IsChildAction这个属性究竟是怎么定义的了。   public virtual bool IsChildAction {
            get {
                RouteData routeData = RouteData;
                if (routeData == null) {
                    return false;
                }
                return routeData.DataTokens.ContainsKey(PARENT_ACTION_VIEWCONTEXT);
            }
        }
而RenderAction实际上市调用  internal static void ActionHelper(HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, TextWriter textWriter) 
各参数如下:actionName:Index
controllerName:Test
routeValues:null
textWriter:htmlHelper.ViewContext.Writer
在这个方法里面有一句   RouteData routeData = CreateRouteData(vpd.Route, routeValues, vpd.DataTokens, htmlHelper.ViewContext);该方法的代码如下:    private static RouteData CreateRouteData(RouteBase route, RouteValueDictionary routeValues, RouteValueDictionary dataTokens, ViewContext parentViewContext) {
            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;
        }我想看到这里大家都应该明白了吧,TempData也可次访问。应该是说MVC在请求周期结束的时候有动作去删除此类的Session,而不是访问一次就被删除。MS命名为TempData,意思应该是说TempData是个Session,但是它又和普通的Session不同。它会在请求之后被删除,所以是临时的Data


热点排行