说明: 原文作者贤新

   原文地址:http://www.cnblogs.com/chenxinblogs/p/4852813.html

ViewData和ViewBag主要用于将数据从控制器中传递到视图中去,ViewData本身就是一个字典。以KeyValue的形式存取值。ViewData的Value类型是Object,也就是可以将任意类型的值存储到ViewData中去,平时我们都在控制器中直接使用ViewData.本质上ViewData只是Controller父类ControllerBase中的一个属性,其类型是ViewDataDictionary,因为我们在自己的Controller中并未定义一个叫做ViewData的属性,也就是说当我们访问在某个类的属性或者方法中所访问的某个方法或者属性中没有找到时,我们就要想到这个属性或者方法是否在父类中已经定义了,这个对于一个新手来说往往是容易忽略的,TempData是用于解决在不同的的Action方法之间跳转的时候的数据传递。这里不同的Action可以是同一个Controller下的不同的Action之间,也可以是不同Controller的Action之间。有些人说,利用Session不是也可以实现吗?是的,没错,不过仔细的去看下微软的Mvc源码,你会发现,其实TempData中的数据的维护也是用到了Session的。

ViewData

    我们很经常看到这样,

     public ActionResult Index()
{
//从数据库中读取产品列表
List<Product> productList = db.Products.ToList();   
       //这个时候productList这个集合对象将被传递到视图页中去.       
       //如果此时,在视图页面中使用@model List<Product> 声名,你会发现在视图页面中,直接访问View       
       //注意,每个视图页面在网站第一次被请求时,都会被编译成一个对应的       
       //一个类,这些视图类都会被编译到一个临时的程序集中去,这个临时的程序集的位置,可以通过在视图页面中编写代码@this.GetType().Assembly.Locaiton       
       //的方式来查看其位置,实际上就是在C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\vs的某个目录下。       
       //这个类直接或者间接继承自:System.Web.Mvc.WebViewPage类.
return View(productList);
}

      细心的查看View()方法的源码:

protected internal virtual ViewResult View(string viewName, string masterName, object model)
{
if (model != null)
{
     //看到了没有,其实就是直接将我们想要传递到视图页的数据,保存到了父类ControllerBase的ViewData属性对象的一个名为Model的属性中去了。
     //然后在将Controller中的ViewData的引用传递给视图页面类的实例对象上的ViewData属性,这样就能将在控制器的Action中往ViewData中设置的值传递到视图中去了,也就是我们上面的代码还可以改为
    //ViewData.Model=productList;return View();就行了。
base.ViewData.Model = model;
}
return new ViewResult { ViewName = viewName, MasterName = masterName, ViewData = base.ViewData, TempData = base.TempData, ViewEngineCollection = this.ViewEngineCollection };
}

    需要注意的是:ViewData中的数据只能传递到当前这个Action所要去加载的视图页面中去,而不能跨Action传输。

ViewBag

   ViewBag,其实内部真正存储数据的还是ViewData,也就是说,ViewData和ViewBag的数据是共享的,通过ViewData设置的数据,可以通过ViewBag访问,通过ViewBag设置的数据可以通过

ViewData访问。

   看看ControllerBase中的ViewBag属性的源码:

[Dynamic]
public object ViewBag
{
[return: Dynamic]
get
{
Func<ViewDataDictionary> viewDataThunk = null;
if (this._dynamicViewDataDictionary == null)
{
if (viewDataThunk == null)
{
viewDataThunk = () => this.ViewData;
}
//viewDataThunk是个lambda表达式,返回ViewData.也就是DynamicViewDataDictionary内部还是用的是ViewData.
this._dynamicViewDataDictionary = new DynamicViewDataDictionary(viewDataThunk);
}
return this._dynamicViewDataDictionary;
}
}

    以下是DynamicViewDataDictionary中的两个方法。

    public override bool TryGetMember(GetMemberBinder binder, out object result)
{      //看到重点了吧。
result = this.ViewData[binder.Name];
return true;
} public override bool TrySetMember(SetMemberBinder binder, object value)
{
this.ViewData[binder.Name] = value;
return true;
}

TempData

    我们需要知道的是,.NET Mvc中最终处理来自于浏览器端的请求的是一个MvcHandler的类,这个类实现了IHttpHandler,而IHttpHandler中定义了一个ProccessRequest方法,和WebForm

不一样的是,Controller对象的创建是在MvcHandler中来完成的。从MvcHanlder的ProccessRequest方法中开始追踪,我们会发现以下代码:

protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
IController controller;
IControllerFactory factory;
this.ProcessRequestInit(httpContext, out controller, out factory);
try
{
controller.Execute(this.RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
}

    再看看controller.Execute的源码(因为controller变量是IController接口类型,所以要查看实现了IController的类的Execute方法)发现是ControllerBase实现了该接口中的Execute方法,再看看,ControllerBase中的Execute方法,发现调用了自己的ExecuteCore方法,发现ExecuteCore是一个abstract方法,没有方法体,然后我们从其子类Controller中看到了重写了其父类ControllerBase中的ExecuteCore方法,Controller->ExecuteCore方法如下:

protected override void ExecuteCore()
{
//这句代码表示在调用目标Action之前,去Session中加载对应的来自于上个Action   //中保存的传递过来的数据。
this.PossiblyLoadTempData();
try
{
string requiredString = this.RouteData.GetRequiredString("action");
if (!this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString))
{
this.HandleUnknownAction(requiredString);
}
}
finally
{
this.PossiblySaveTempData();
}
}

    我们在看看PosiblyLoadTempData这个方法中的代码,很明显,这个方法就是Controller中的.

internal void PossiblyLoadTempData()
{
//这里说明了,只有当前被请求的不是子Action的时候才会去加载对应的TempData数据,从Session中。
if (!base.ControllerContext.IsChildAction)
{
//TempData的类型是TempDataDictionary,我们看看这个类中的Load方法,查看TempDataDictionary.Load方法,我们发现其实真正去加载的是一个实现了ITempDataProvider接口的某个类的实例对象去加载的。    //其实就是SessionStateTempDataProvider,找到这个类,查看其LoadTempData方法。
base.TempData.Load(base.ControllerContext, this.TempDataProvider);
}
}

    下面是SessionStateTempDataProvider->LoadTempData方法源码:

    // Methods
public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
HttpSessionStateBase session = controllerContext.HttpContext.Session;
if (session != null)
{
     //真相大白了,其实我们从TempData中取数据时,还是从一个key为__ControllerTempData的Session中取出来的,也就是说TempData只是一个临时的数据保存的地方,        //最终在调用Action完毕后,框架自动把在Action中往TempData中设置的值保存到Session中去,然后跳转到下个Action并在这个Action执行之前,又从Session中取出来,       //通过as Dictionary让我们也知道了,其实TempData中保存数据的就是一个普通的字典而已,这就是为什么TempData能在不同的请求之间保存数据,同时也说明了为什么能在多个不同的Action之间无限的进行        //数据传递。
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);
}

   下面是SessionStateTempDataProvider的SaveTempData方法

   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");
}
}

   再回头看看Controller类中的ExecuteCore方法,其实就是在浏览器发出请求之后,调用控制器的某个Action之前,先会去Session中尝试找Key为__ControllerTempData的Session["__ControllerTempData"]是否为null,如果不为null,那么就将其取出,并且转换为Dictionary<string,object>类型,并且存储到ControllerBase父类中的TempData属性对象里的内部属性_data中去,

然后我们在Action中或者视图中取出TempData的值的时候,就是从这个内部字典_data中取值的,在执行完action并且执行完视图页面的代码之后(this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString这句代码会去找到对应的视图页面类,并且去执行,),最后一个步骤,就是去将TempData中内部字典的内容保存到Session中去,因为是最后执行,所以,在视图页面中保存到TempData的值也会被保存起来,以供下次使用,也就是说TempData是跨请求的,但是你会发现如果经过了两次请求,也就是从浏览器中输入两次,你会发现只能取一次,为什么呢,看下面这个TempDataDictionary的Save方法。

public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
{    //也就是保存到Session之前,会去先看看这些key是否是通过我们手动TempData[""]的方式设置进去的    //(之所以这种判断,是因为在使用TempData["key"]=value的时候,索引器的set中包含了一句,this._initialKeys.add(key)的方法)    //如果不是,则不会去再次保存,这就是为什么你在第二个请求的Action中只能取一次的原因了。 //这里说的,是指第三次刷新第二次请求的那个Action时,已经无法访问到第一次请求存进去的TempData中的值了,因为第二次请求的时候,因为这个key没有在_initialKeys和_retainKeys这两个HashSet中。 //如果要继续保留,请在使用TempData中的数据之前,注意:是之前哦,调用Keep()或者Keep(string key)方法。
this._data.RemoveFromDictionary<string, object, TempDataDictionary>(delegate (KeyValuePair<string, object> entry, TempDataDictionary tempData) {
string item = entry.Key;
return !tempData._initialKeys.Contains(item) && !tempData._retainedKeys.Contains(item);
}, this);
tempDataProvider.SaveTempData(controllerContext, this._data);
}

   为什么会在使用一次之后,就不会在保存回Session中去了呢,如果,还不够清楚,还可以看看,TempData的索引器,可以发现get下有一个关键代码:

    public object this[string key]
{
get
{
object obj2;
if (this.TryGetValue(key, out obj2))
{          //原来这边在我们取数据的时候,将将key从_initialKeys中移除了,当TempData.Save方法被调用时,发现_data字典中的我们取数据的那个key不在这个HashSet中,所以就不会被保存了。          //如果你希望取数据了之后,又希望还能传递到下一个action用的话,那么请使用Peek方法。
this._initialKeys.Remove(key);
return obj2;
}
return null;
}
set
{
this._data[key] = value;        //这句说明了,为什么我们使用TempData["key"]=value设置的值,可以留到下次使用的原因。
this._initialKeys.Add(key);
}
}
 

.NET MVC TempData、ViewData、ViewBag的更多相关文章

  1. MVC传递Model之TempData、ViewData、ViewBag差别及用途

    MVC使用过程中常常会用到TempData.ViewData.ViewBag三种方式,这三种什么差别呢? TempData:默认存储于Session中,可通过继承ITempDataProvider接口 ...

  2. MVC中的ViewData、ViewBag和TempData

    一.ViewBag和ViewData的定义 public dynamic ViewBag { get; } public ViewDataDictionary ViewData { get; set; ...

  3. ASP.NET MVC 传值方法ViewData与ViewBag的区别

    一.介绍 在Asp.net MVC 3 web应用程序中,我们会用到ViewData与ViewBag,对比一下: ViewData ViewBag 它是Key/Value字典集合 它是dynamic类 ...

  4. Asp.net MVC中的ViewData与ViewBag

    Asp.net MVC中的ViewData与ViewBag 在Asp.net MVC 3 web应用程序中,我们会用到ViewData与ViewBag,对比一下: ViewData ViewBag 它 ...

  5. 浅谈 MVC中的ViewData、ViewBag和TempData

    ViewBag和TempData的区别 ViewData ViewBag 它是Key/Value字典集合 它是dynamic类型对像 从Asp.net MVC 1 就有了 ASP.NET MVC3 才 ...

  6. asp.net mvc之TempData、ViewData、ViewBag

    ★ViewData和ViewBag:生命周期相同,仅对当前View有效,不同的是ViewBag的类型不是字典的键值对结构,而是dynamic动态类型. ViewData ViewBag Key/Val ...

  7. ViewModel、ViewData、ViewBag、TempData、Session之间的区别和各自的使用方法

    ViewModel    ViewModel 是一个用来渲染 ASP.NET MVC 视图的强类型类,可用来传递来自一个或多个视图模型(即类)或数据表的数据.可将其看做一座连接着模型.数据和视图的桥梁 ...

  8. MVC4.0中ViewBag、ViewData、TempData和ViewModel几种传值方式的区别

    MVC框架实现了数据的分离,使页面看起来更加的简洁,MVC4.0中Controller和View的数据传输有上边这几种方式,今天我们来探讨下这几种方式的却别. 一:ViewBag和ViewData V ...

  9. .NET/MVC-ViewBag、ViewData、TempData区别

    1.ViewData ViewData的生命周期和View相同, 只对当前View有效. 2.TempData TempData保存在Session中, Controller每次执行请求的时候会从Se ...

随机推荐

  1. RDD常用方法之subtract&intersection&cartesian

    subtract Return an RDD with the elements from `this` that are not in `other` .     def subtract(othe ...

  2. C++设计模式-Prototype原型模式

    作用: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. Prototype模式提供了一个通过已存在对象进行新对象创建的接口(Clone), Clone()实现和具体的语言相关,在C+ ...

  3. Asprise-OCR的使用

    Asprise-OCR下载地址: http://asprise.com/product/ocr/download.php?lang=csharp 其中需要使用的3个dll是AspriseOCR.dll ...

  4. 用iptables 实现本地端口转发

    设定本机2121端口转发到21端口 iptables -t nat -A PREROUTING -p tcp -i eth0 -d -j DNAT --to iptables -t nat -I PO ...

  5. 微信nickname乱码及mysql编码格式设置(utf8mb4)

    微信nickname乱码及mysql编码格式设置(utf8mb4) 今天在写微信公众平台项目时,写到一个用户管理模块,接口神马的已经调试好了,于是将用户从微信服务器保存到本地数据库,发现报错: jav ...

  6. Find out files transfered via Bluetooth

    The case was about business secret and forensic guy did a physical acquisition from a smart phone. H ...

  7. win7删除一些顽固的文件夹

    创建一个记事本,键入以下命令: DEL /F /A /Q \\?\%1 RD /S /Q \\?\%1 然后保存为bat文件,然后将要删除的文件或文件夹拖入bat的文件图标上,既可以强力删除一些无法删 ...

  8. matlab 去掉字符串前后的空格

    strtrim 从字符串,删除开头和结尾的空白 句法 S = strtrim(STR) C = strtrim(CSTR)

  9. apache配置多域名多站点记录

    <VirtualHost *:80>  DocumentRoot "/mnt/web/www.*.cn"  ServerName www.*.cn  ErrorLog ...

  10. vim编辑器详解

    vi(vim)是上Linux非常常用的编辑器,很多Linux发行版都默认安装了vi(vim).vi(vim)命令繁多但是如果使用灵活之后将会大大提高效率. vi是“visual interface”的 ...