asp.net mvc 之旅 —— 第五站 从源码中分析asp.net mvc 中的TempData
来源:互联网 发布:做淘宝天猫优惠券 编辑:程序博客网 时间:2024/06/05 10:10
基本上是一个东西,就是各自的编程写法不一样,最终都会放到viewContext中,然后送到WebPage中,如果你要证明的话,可以看下下面的代码。
/// <summary>Gets the dynamic view data dictionary.</summary> /// <returns>The dynamic view data dictionary.</returns> [Dynamic] public dynamic ViewBag { [return: Dynamic] get { if (this._dynamicViewDataDictionary == null) { this._dynamicViewDataDictionary = new DynamicViewDataDictionary(() => this.ViewData); } return this._dynamicViewDataDictionary; } } /// <summary>Gets or sets the dictionary for view data.</summary> /// <returns>The dictionary for the view data.</returns> public ViewDataDictionary ViewData { get { if (this._viewDataDictionary == null) { this._viewDataDictionary = new ViewDataDictionary(); } return this._viewDataDictionary; } set { this._viewDataDictionary = value; } }
从上面的代码中可以看到,其实ViewBag就是获取ViewData的数据,对不对。。。
一:TempData
至于这个东西怎么用,大家貌似都记得是可访问一次后即刻消失,好像貌似也就这样了,当然不知道有没有人对tempdata的底层代码进行研究呢???
看一下它的底层到底是怎么来实现的。
1. TempData源代码
首先我们看一下TempData的类型是TempDataDictionary,可以看到这个类型肯定是实现了IDictionary接口的自定义字典,
public TempDataDictionary TempData { get { if (this.ControllerContext != null && this.ControllerContext.IsChildAction) { return this.ControllerContext.ParentActionViewContext.TempData; } if (this._tempDataDictionary == null) { this._tempDataDictionary = new TempDataDictionary(); } return this._tempDataDictionary; } set { this._tempDataDictionary = value; } }
从上面代码可以看到,tempdate默认是new了一个TempDataDictionary类,这个类中很好玩的地方在于这里有一个load方法,这个load方法就是获取真
正的provider,比如下面这样:
/// <summary>Loads the specified controller context by using the specified data provider.</summary> /// <param name="controllerContext">The controller context.</param> /// <param name="tempDataProvider">The temporary data provider.</param> public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider) { IDictionary<string, object> dictionary = tempDataProvider.LoadTempData(controllerContext); this._data = ((dictionary != null) ? new Dictionary<string, object>(dictionary, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)); this._initialKeys = new HashSet<string>(this._data.Keys, StringComparer.OrdinalIgnoreCase); this._retainedKeys.Clear(); }
这个load方法就是非常重要的,这里的参数ITempDataProvider就是我们在BeginExecute方法赋值的,继续往下看,不要着急哦。。。
2. BeginExecute
我们知道,mvc框架其实是截获了mvcroutehandler来进行截获url的请求,继而将后续的处理就由mvc框架来接管,最终会执行到Controller类下面的
BeginExecute,如果你不信,我可以开心加愉快的给你上代码,比如下面这样:
protected virtual IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state) { Action action2 = null; if (this.DisableAsyncSupport) { if (action2 == null) { action2 = delegate { this.Execute(requestContext); }; } Action action = action2; return AsyncResultWrapper.BeginSynchronous(callback, state, action, _executeTag); } if (requestContext == null) { throw new ArgumentNullException("requestContext"); } base.VerifyExecuteCalledOnce(); this.Initialize(requestContext); BeginInvokeDelegate<Controller> beginDelegate = (asyncCallback, callbackState, controller) => controller.BeginExecuteCore(asyncCallback, callbackState); EndInvokeVoidDelegate<Controller> endDelegate = delegate (IAsyncResult asyncResult, Controller controller) { controller.EndExecuteCore(asyncResult); }; return AsyncResultWrapper.Begin<Controller>(callback, state, beginDelegate, endDelegate, this, _executeTag, -1, null); }
上面这段代码中,你一定要看清楚上面标红的地方,这里我们看到了,其实这里是一个异步的beginxxx,endxxx的操作,问题就是在这里,首先我们从
beginInvoke说起。
<1> beginDelegate
这个异步操作中,我们可以看到,其实执行的是一个controller.BeginExecuteCore(asyncCallback, callbackState) 方法,对吧,然后我们可以
感兴趣的看一下这个方法干了什么?
protected virtual IAsyncResult BeginExecuteCore(AsyncCallback callback, object state) { IAsyncResult result; this.PossiblyLoadTempData(); try { Action action2 = null; string actionName = GetActionName(this.RouteData); IActionInvoker invoker = this.ActionInvoker; IAsyncActionInvoker invoker = invoker as IAsyncActionInvoker; if (invoker != null) { BeginInvokeDelegate<ExecuteCoreState> beginDelegate = (asyncCallback, asyncState, innerState) => innerState.AsyncInvoker.BeginInvokeAction(innerState.Controller.ControllerContext, innerState.ActionName, asyncCallback, asyncState); EndInvokeVoidDelegate<ExecuteCoreState> endDelegate = delegate (IAsyncResult asyncResult, ExecuteCoreState innerState) { if (!innerState.AsyncInvoker.EndInvokeAction(asyncResult)) { innerState.Controller.HandleUnknownAction(innerState.ActionName); } }; ExecuteCoreState invokeState = new ExecuteCoreState { Controller = this, AsyncInvoker = invoker, ActionName = actionName }; return AsyncResultWrapper.Begin<ExecuteCoreState>(callback, state, beginDelegate, endDelegate, invokeState, _executeCoreTag, -1, null); } if (action2 == null) { action2 = delegate { if (!invoker.InvokeAction(this.ControllerContext, actionName)) { this.HandleUnknownAction(actionName); } }; } Action action = action2; result = AsyncResultWrapper.BeginSynchronous(callback, state, action, _executeCoreTag); } catch { this.PossiblySaveTempData(); throw; } return result; }
从上面的代码中,你应该看到了有一个 this.PossiblyLoadTempData()方法,看这个名字我们大概就可以猜得到这个方法和tempdate肯定有莫大的关系。
说时迟那时快,我们可以看下这个方法到底干了什么。。。在一系列跟踪之后,我们最后会到这个代码里面去了,如下所示:
internal void PossiblyLoadTempData() { if (!base.ControllerContext.IsChildAction) { base.TempData.Load(base.ControllerContext, this.TempDataProvider); } }
请大家看清了,这里我们调用了刚才文章开头出说到的Tempdata.Load方法,那么问题来了,这里的TempDataProvider到底是怎么来的。我们继续来看代码:
public ITempDataProvider TempDataProvider { get { if (this._tempDataProvider == null) { this._tempDataProvider = this.CreateTempDataProvider(); } return this._tempDataProvider; } set { this._tempDataProvider = value; } }
看到没有,然后TempDataProvider然来是调用了CreateTempDataProvider方法来实现的,下一步我们来看一下CreateTempDataProvider到底干了什么。
protected virtual ITempDataProvider CreateTempDataProvider() { ITempDataProviderFactory service = this.Resolver.GetService<ITempDataProviderFactory>(); if (service != null) { return service.CreateInstance(); } return (this.Resolver.GetService<ITempDataProvider>() ?? new SessionStateTempDataProvider()); }
从上面这个代码,我们应该就明白了,然来我们的tempdata默认是由SessionStateTempDataProvider来提供的,好了,接下来我们就可以继续看看
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> dictionary = session["__ControllerTempData"] as Dictionary<string, object>; if (dictionary != null) { session.Remove("__ControllerTempData"); return dictionary; } } 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 flag = (values != null) && (values.Count > 0); if (session == null) { if (flag) { throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled); } } else if (flag) { session["__ControllerTempData"] = values; } else if (session["__ControllerTempData"] != null) { session.Remove("__ControllerTempData"); } } }
可以看到,SessionStateTempDataProvider 是实现了ITempDataProvider接口,里面有两个方法LoadTempData 和SaveTempData方法,而
LoadTempData方法的逻辑很奇葩,你可以仔细观察一下哦,如果 if (session != null)满足就清空字典的数据,否则就不清除,这个逻辑大概就向
你展示了为什么数据只能被读取一次,下次读取的时候,就走了这个if(session!=null)给清空了,你怎么可能再读取session中的数据呢。。。这个
就是为什么tempdata只能被读取一次的真相,是不是很好玩。
<2> EndExecuteCore
有人可能会问了,第二个方法SaveTempData是什么时候执行的,当然就是EndExecuteCore里面了,比如你看:
protected virtual void EndExecuteCore(IAsyncResult asyncResult) { try { AsyncResultWrapper.End(asyncResult, _executeCoreTag); } finally { this.PossiblySaveTempData(); } }
可以看到它的默认实现是session,当然你也可以实现一个自定义的provider,比如用cache来存放这个临时数据,或者是redis,mongodb等等。。。
当然还有更多有趣的东西等待你发掘哦~~~
- asp.net mvc 之旅 —— 第五站 从源码中分析asp.net mvc 中的TempData
- asp.net mvc源码分析-Controllerl篇 TempData数据存储
- ASP.NET MVC 入门6、TempData
- ASP.NET MVC 入门6、TempData
- ASP.NET MVC 入门6、TempData
- ASP.NET MVC 入门 -- 6、TempData
- ASP.NET MVC 入门6、TempData
- ASP.NET MVC 入门 6 TempData
- ASP.NET MVC 入门5 TempData
- ASP.NET MVC 入门6、TempData
- ASP.NET MVC 入门6、TempData
- ASP.NET MVC 入门6、TempData
- Asp.Net Mvc: 浅析TempData机制
- asp.net mvc中TempData和ViewData的区别
- ASP.Net MVC中 ViewBag, ViewData, TempData, ViewModel 的区别
- Asp.net MVC中ViewData、ViewBag和TempData区别
- asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析
- Asp.net MVC中的"??"
- 几个资料地址保存
- 几个开源的视频编解码器介绍
- STL泛型编程
- 分布式架构中一致性解决方案——Zookeeper集群搭建
- java爬虫crawler4j:SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
- asp.net mvc 之旅 —— 第五站 从源码中分析asp.net mvc 中的TempData
- javascript
- linux多进程通讯
- asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析
- mongodb 3.x 之实用新功能窥看[1] ——使用TTLIndex做Cache处理
- bzoj1045: [HAOI2008] 糖果传递
- Linux(Centos)安装tomcat
- 机器学习资料分享
- mongodb 3.x 之实用新功能窥看[2] ——使用$lookup做多表关联处理