在mvc的controller中,我们知道有很多的临时变量存放数据,比如说viewData,viewBag,还有一个比较特殊的tempData,关于前两个或许大家都明白,

基本上是一个东西,就是各自的编程写法不一样,最终都会放到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, -, 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, -, 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 > );
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的更多相关文章

  1. Sql Server之旅——第五站 确实不得不说的DBCC命令

    原文:Sql Server之旅--第五站 确实不得不说的DBCC命令 今天研发中心办年会,晚上就是各自部门聚餐了,我个人喜欢喝干红,在干红中你可以体味到那种酸甜苦辣...人生何尝不是这样呢???正好 ...

  2. Asp.net MVC集成Google Calendar API(附Demo源码)

    Asp.net MVC集成Google Calendar API(附Demo源码) Google Calendar是非常方便的日程管理应用,很多人都非常熟悉.Google的应用在国内不稳定,但是在国外 ...

  3. Spring源码深度解析之Spring MVC

    Spring源码深度解析之Spring MVC Spring框架提供了构建Web应用程序的全功能MVC模块.通过策略接口,Spring框架是高度可配置的,而且支持多种视图技术,例如JavaServer ...

  4. ASP.NET中登录时记住用户名和密码(附源码下载)--ASP.NET

    必需了解的:实例需要做的是Cookie对象的创建和对Cookie对象数据的读取,通过Response对象的Cookies属性创建Cookie,通过Request对象的Cookies可以读取Cookie ...

  5. Flask框架(五) —— session源码分析

    Flask框架(五) —— session源码分析 目录 session源码分析 1.请求来了,执行__call__方法 2.__call__方法 3.调用__call__方法 3.1.ctx = s ...

  6. asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析

    这篇文章我们开始看一下ActionFilter,从名字上其实就大概知道ActionFilter就是Action上的Filter,对吧,那么Action上的Filter大概有几个呢??? 这个问题其实还 ...

  7. asp.net mvc 之旅—— 第三站 路由模板中强大的自定义IRouteConstraint约束

    我们在写mvc的时候,经常会配置各种url模板,比如controller,action,id 组合模式,其实呢,我们还可以对这三个参数进行单独的配置,采用的方式自然 就是MapRoute中的const ...

  8. 源码学习之ASP.NET MVC Application Using Entity Framework

    源码学习的重要性,再一次让人信服. ASP.NET MVC Application Using Entity Framework Code First 做MVC已经有段时间了,但看了一些CodePle ...

  9. Profession ASP.NET MVC 2.0 NerdDinner示例可运行源码

    最近一段时间在看JonGalloway等著作的<Profession ASP.NET MVC 2.0>.本书并没有按照常规的大部头书籍那样,按部就班的介绍MVC的概念等,而是在第一章直接引 ...

随机推荐

  1. ABP(现代ASP.NET样板开发框架)系列之9、ABP设置管理

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之9.ABP设置管理 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  2. ABP框架 - 介绍

    文档目录 本节内容: 简介 一个快速示例 其它特性 启动模板 如何使用 简介 我们总是对不同的需求开发不同的应用.但至少在某些层面上,一次又一次地重复实现通用的和类似的功能.如:授权,验证,异常处理, ...

  3. Lind.DDD.Plugins~插件模式的集成

    回到目录 对于Lind.DDD这个敏捷框架来说,插件也是其中的一个亮点,所有被认为是插件(Plugins)的模块都会继承自IPlugins这个标示接口,它在程序启动时会找到所有插件,并通过autofa ...

  4. jQuery 根据值或者文本选中select

    今天因为有项目需要动态操作select选中 习惯在百度上搜了一下 ,结果还是挺多的.试了其中一个 发现不能使用.打开第2,3 个发现都是一样的然后自己稍微研究了一下 //初始化select,第一个选中 ...

  5. 搭建LNAMP环境(七)- PHP7源码安装Memcached和Memcache拓展

    上一篇:搭建LNAMP环境(六)- PHP7源码安装MongoDB和MongoDB拓展 一.安装Memcached 1.yum安装libevent事件触发管理器 yum -y install libe ...

  6. sqlalchemy学习

    sqlalchemy官网API参考 原文作为一个Pythoner,不会SQLAlchemy都不好意思跟同行打招呼! #作者:笑虎 #链接:https://zhuanlan.zhihu.com/p/23 ...

  7. VS2015企业版本(安装包+key)+ .NET Reflector 9.0

    Reflector9.0系下载:https://yunpan.cn/cMQj9zWbffSqy  访问密码 55eb VS2015中文企业版: http://pan.baidu.com/s/1eRIo ...

  8. 安装nodejs express框架时express命令行无效

    我也是看了这篇才明白.http://jingyan.baidu.com/article/922554468a3466851648f419.html 最近在看一本书,nodejs开发指南.至于出现这个问 ...

  9. [Maven]Maven入门教程

    概念 Maven是什么 Maven 是一个项目管理工具.它负责管理项目开发过程中的几乎所有的东西. 版本 maven有自己的版本定义和规则 构建 maven支持许多种的应用程序类型,对于每一种支持的应 ...

  10. request 对象和 response 对象

    Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象 HttpServletResponse HttpServletR ...