1.ViewData, ViewBag, TempData
viewData是ViewDataDictionary字典类型 它实现了 IDictionary<string, object>, ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>, IEnumerable接口
ViewBag是动态类型,编译时不会检查,只有运行时才会检查,“运行时变量” 它本质和ViewData是一样的。
TempData
1,是一个string object的字典
2,Action执行前后都会对temp进行操作
我们的TempData其实是用System.Web.Mvc.SessionStateTempDataProvider来实现的
TempData可以存储一次,只能读取一次,如果第二次读取,将不会有tempdata数据,这样就起到了临时变量的作用
重点:控制器在调用一个Action方法前,会先执行一个BeginExecuteCore方法 。这个方法中的执行了一个this.PossiblyLoadTempData()方法 ,这个方法最终执行的是System.Web.Mvc.SessionStateTempDataProvider类中的LoadTempData方法
//控制器的Action方法执行前会执行一个BeginExecuteCore方法
protected virtual IAsyncResult BeginExecuteCore(AsyncCallback callback, object state)
{
this.PossiblyLoadTempData(); //这个方法调用的是SessionStateTempDataProvider类中的LoadTempData方法
..........
}
而控制在调用完Action方法并执行完视图后(即在OnResultExecuted 方法后)执行了SaveTempData方法。这个方法中执行了this.PossiblySaveTempData()方法,这个方法最终执行的是System.Web.Mvc.SessionStateTempDataProvider类中的SaveTempData方法
//控制器调用Action方法并在视图执行完毕后会执行一个EndExecuteCore方法(即这个方法会在Action过滤器的OnResultExecuted方法执行完后再执行)
protected virtual void EndExecuteCore(IAsyncResult asyncResult)
{
try
{
AsyncResultWrapper.End(asyncResult, Controller._executeCoreTag);
}
finally
{
this.PossiblySaveTempData(); //这个方法调用的是SessionStateTempDataProvider类中的SaveTempData方法
}
}
以下System.Web.Mvc.SessionStateTempDataProvider类中的LoadTempData方法和SaveTempData方法两个方法就是TempData的原理机制。
让我们来看看System.Web.Mvc.SessionStateTempDataProvider类中的LoadTempData方法和SaveTempData方法
namespace System.Web.Mvc
{
/// <summary>Provides session-state data to the current <see cref="T:System.Web.Mvc.TempDataDictionary" /> object.</summary>
public class SessionStateTempDataProvider : ITempDataProvider
{
internal const string TempDataSessionStateKey = "__ControllerTempData";
/// <summary>Loads the temporary data by using the specified controller context.</summary>
/// <returns>The temporary data.</returns>
/// <param name="controllerContext">The controller context.</param>
/// <exception cref="T:System.InvalidOperationException">An error occurred when the session context was being retrieved.</exception>
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["__ControllerTempData"] 存不存在,如果存在,则把这个Session删除
session.Remove("__ControllerTempData"); //注意:虽然删除了session,但是dictionary已经有值了哦
return dictionary;//返回这个字典
}
}
return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
}
/// <summary>Saves the specified values in the temporary data dictionary by using the specified controller context.</summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="values">The values.</param>
/// <exception cref="T:System.InvalidOperationException">An error occurred the session context was being retrieved.</exception>
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;
return;
}
if (session["__ControllerTempData"] != null)
{
session.Remove("__ControllerTempData");
}
}
}
}
}
根据原理让我们来自己实现TempData (我们知道TempData是根据Session来实现的,既然是Session,那么模式是线程方式,,这样的Session是无法进行跨平台的
假设我们的项目采用的分布式部署,MVC在多台机器中部署..那么怎么做到Session在多台机器中共存呢,这就涉及到分布式的存储。比如存储在关系型数据库中 如Sql Server或者MySql 又比如存储在非关系行数据库中,如MongoDB,Redis中。)
为了演示,我们就用本地的Cache来替换Session吧。。重点在替换,不在实现。
第一:首先定义一个自己的类(MyCacheStateTempData),实现ITempDataProvider接口,这个接口中就定义了LoadTempData和SaveTempData两个方法
第二:然后我需要定义自己的控制工厂。并在Global文件中将自己定义的控制器工厂替换掉MVC默认的控制器工厂
第三:在自定义的控制器工厂中将控制器的TempDataProvider属性设置为我们的自定义的 MyCacheStateTempData类对象
第四:动手写代码
namespace MvcApp
{
// 注意: 有关启用 IIS6 或 IIS7 经典模式的说明,
// 请访问 http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//将默认的控制器工厂修改为我们自己的控制器工厂
ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
}
}
//我们只所以要自己定义一个我们自己的控制器工厂类,目的是就是要将控制器的TempDataProvider值设为我们自己定义的MyCacheStateTempData对象
public class MyControllerFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext requestContext, string controllerName)
{
//调用父类的CreateController创建控制器。与默认的其实是一模一样的
var iController = base.CreateController(requestContext, controllerName);
var controller = iController as Controller; //类型转换
//注:控制的的TempDataProvider的默认值是 new SessionStateTempDataProvider()
//现在我们将它修改为我们自己定义的值
controller.TempDataProvider = new MyCacheStateTempData();
return iController;
}
}
//这个类就是实现了Mvc的TempData的机制
//【这个类中的两个方法我模仿了Mvc默认的实现机制,只是将controllerContext.HttpContext.Session改为了controllerContext.HttpContext.Cache】
public class MyCacheStateTempData : ITempDataProvider
{
internal const string TempDataSessionStateKey = "__ControllerTempData";
//控制器调用Action方法前会执行这个LoadTempData方法
public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
var cache = controllerContext.HttpContext.Cache;
if (cache != null)
{
Dictionary<string, object> dictionary = cache["__ControllerTempData"] as Dictionary<string, object>;
if (dictionary != null)
{
//第一次进来,查询下session["__ControllerTempData"] 存不存在,如果存在,则把这个Session删除
cache.Remove("__ControllerTempData"); //注意:虽然删除了session,但是dictionary已经有值了哦
return dictionary;//返回这个字典
}
}
return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
}
//控制器调用Action方法后会执行这个SaveTempData方法。目的是将我们在Action方法中设置的TempData["username"] = "jack";保存起来
public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
var cache = controllerContext.HttpContext.Cache;
bool flag = values != null && values.Count > 0;
if (cache == null)
{
if (flag)
{
throw new Exception("这里抛了个异常");
}
}
else
{
if (flag)
{
cache["__ControllerTempData"] = values;
return;
}
if (cache["__ControllerTempData"] != null)
{
cache.Remove("__ControllerTempData");
}
}
}
}
}
OK