在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. 解析大型.NET ERP系统核心组件 查询设计器 报表设计器 窗体设计器 工作流设计器 任务计划设计器

    企业管理软件包含一些公共的组件,这些基础的组件在每个新项目立项阶段就必须考虑.核心的稳定不变功能,方便系统开发与维护,也为系统二次开发提供了诸多便利.比如通用权限管理系统,通用附件管理,通用查询等组件 ...

  2. ★Kali信息收集~4.DNS系列

    ★.1host:DNS信息 参数: 一般情况下,host查找的是A,AAAA,和MX的记录 案例: DNS服务器查询  host -t ns 域名 A记录和MX记录查询  host 域名(host - ...

  3. 阿里云自定义日记文件无法通过ftp下载

    异常处理汇总 ~ 修正果带着你的Net飞奔吧!http://www.cnblogs.com/dunitian/p/4599258.html 有可能是个例,xftp不行(对linux支持很好),Cute ...

  4. Composer概述及其自动加载探秘

    composer概述 一开始,最吸引我的当属 Composer 了,因为之前从没用过 Composer . Composer 是PHP中用来管理依赖关系的工具,你只需在自己的项目中声明所依赖的外部工具 ...

  5. ElasticSearch 5学习(5)——第一个例子(很实用)

    想要知道ElasticSearch是如何使用的,最快的方式就是通过一个简单的例子,第一个例子将会包括基本概念如索引.搜索.和聚合等,需求是关于公司管理员工的一些业务. 员工文档索引 业务首先需要存储员 ...

  6. UploadFile控件,提交图片后,页面预览显示刚刚提交的图片

    最近在用asp.net来写一个新闻系统后台,然后由于不用用网上的flash插件来上传图片什么的,我就用asp.net的控件来写,但是控件总归有一些用的不够灵活的地方.这次测试提出,文章在修改的时候,需 ...

  7. 设计模式(十三):从“FQ”中来认识代理模式(Proxy Pattern)

    我们知道Google早就被墙了,所以FQ才能访问Google呢,这个“FQ”的过程就是一个代理的过程.“代理模式”在之前的博客中不止一次的提及过,之前的委托回调就是代理模式的具体应用.今天我们就从“F ...

  8. EasyUI中那些不容易被发现的坑——EasyUI重复请求2次的问题

    问题控件:datagrid.combobox.所有能设置url属性的控件 问题版本:1.4.4.1.4.5(之前的版本没测) 问题如图: 重复请求2次,错误代码如图: 错误问题分析:html加载的时候 ...

  9. 浅谈css的栅格布局

    栅格布局想必大家都很了解,我们做页面开发的时候,往往对页面板式的要求很高,如何对各个区域的内容排版,并使之对齐是我们的一大难题.而栅格系统就是我们排版的利器,他支持自动对齐.自动计算边距.流式布局等优 ...

  10. [未完成]scikit-learn一般实例之九:用于随机投影嵌入的Johnson–Lindenstrauss lemma边界

    Johnson–Lindenstrauss 引理表明任何高维数据集均可以被随机投影到一个较低维度的欧氏空间,同时可以控制pairwise距离的失真. 理论边界 由一个随机投影P所引入的失真是确定的,这 ...