一:TempData的自定义实现。。。
TempData是用Session实现的,既然是Session,那模式是线程方式。。。这样的Session是没法进行跨平台的。。。
那么这就涉及到如何在多台机器上部署、存储...
- 关系型数据库: sqlserver/mysql
- nosql: mongodb,redis。
问题1:重点在替换,不在实现。。。。。怎么替换,以及使用方式
TempData继承关系:Tempdata => TempDataDictionary=> SessionStateTempDataProvider=>ITempDataProvider
1 namespace System.Web.Mvc 2 { 3 using System; 4 using System.Collections.Generic; 5 using System.Web; 6 using System.Web.Mvc.Properties; 7 8 public class SessionStateTempDataProvider : ITempDataProvider 9 { 10 internal const string TempDataSessionStateKey = "__ControllerTempData"; 11 12 public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext) 13 { 14 HttpSessionStateBase session = controllerContext.HttpContext.Session; 15 if (session != null) 16 { 17 Dictionary<string, object> dictionary = session["__ControllerTempData"] as Dictionary<string, object>; 18 if (dictionary != null) 19 { 20 session.Remove("__ControllerTempData"); 21 return dictionary; 22 } 23 } 24 return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); 25 } 26 27 public virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values) 28 { 29 if (controllerContext == null) 30 { 31 throw new ArgumentNullException("controllerContext"); 32 } 33 HttpSessionStateBase session = controllerContext.HttpContext.Session; 34 bool flag = (values != null) && (values.Count > 0); 35 if (session == null) 36 { 37 if (flag) 38 { 39 throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled); 40 } 41 } 42 else if (flag) 43 { 44 session["__ControllerTempData"] = values; 45 } 46 else if (session["__ControllerTempData"] != null) 47 { 48 session.Remove("__ControllerTempData"); 49 } 50 } 51 } 52 }
SessionStateTempDataProvider类实现了ITempDataProvider接口,重写了Load和Save方法。
从源码可知,SessionStatesTempDataProvider暴露了LoadTempData和SaveTempData两个方法。
其中从SaveTempData中session["__ControllerTempData"] = (object) values;可以看出,TempData是存储在Session中的。
其中LoadTempData方法中session.Remove("__ControllerTempData");就说明了从session中获取tempdata后,对应的tempdata就从session中清空了
问题2:到底在mvc中哪个地方进行注入????mvc的管道中是怎么注入的???
MVC的管道和action方法执行前后发现:PossiblyLoadTempData和PossiblySaveTempData是在调用Controller中对应的action方法时执行的,并且Controller中有 TempDataProvider属性,代码如下:
1 public ITempDataProvider TempDataProvider 2 { 3 get 4 { 5 if (this._tempDataProvider == null) 6 { 7 this._tempDataProvider = this.CreateTempDataProvider(); 8 } 9 return this._tempDataProvider; 10 } 11 set 12 { 13 this._tempDataProvider = value; 14 } 15 }
所以注入点就找到了,在创建Controller Factory中创建Controller实例的时候,把自定义的DataProvider类,赋值给TempDataProvider就可以了
下面来实现一把分布式的tempData
实现分布式流程
继承自DefaultControllerFactory的MyControllerFactory类即自定义的Controller Factory
1 public class MyControllerFactory:DefaultControllerFactory 2 { 3 public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) 4 { 5 var iController= base.CreateController(requestContext, controllerName); 6 7 var controller = iController as Controller; 8 controller.TempDataProvider = new CrossSessionTempData2(); 9 10 11 return iController; 12 } 13 }
TempData的值存入到cache中之文件依赖
接着我们需要自定义一个实现了ITempDataProvider接口的DataProvider类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Caching; 6 using System.Web.Mvc; 7 8 namespace CrossSessionTempData.Infrastructure 9 { 10 public class CrossSessionTempData2 : ITempDataProvider 11 { 12 13 internal const string TempDataSessionStateKey = "__ControllerTempData"; 14 15 public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext) 16 { 17 var cache = controllerContext.HttpContext.Cache; 18 19 if (cache != null) 20 { 21 Dictionary<string, object> dictionary = cache[TempDataSessionStateKey] as Dictionary<string, object>; 22 if (dictionary != null) 23 { 24 //cache.Remove(TempDataSessionStateKey); 25 return dictionary; 26 } 27 } 28 return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); 29 } 30 31 /// <summary>Saves the specified values in the temporary data dictionary by using the specified controller context.</summary> 32 /// <param name="controllerContext">The controller context.</param> 33 /// <param name="values">The values.</param> 34 /// <exception cref="T:System.InvalidOperationException">An error occurred the session context was being retrieved.</exception> 35 public virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values) 36 { 37 if (controllerContext == null) 38 { 39 throw new ArgumentNullException("controllerContext"); 40 } 41 var cache = controllerContext.HttpContext.Cache; 42 bool flag = values != null && values.Count > 0; 43 if (cache == null) 44 { 45 if (flag) 46 { 47 throw new InvalidOperationException(""); 48 } 49 } 50 else 51 { 52 CacheDependency dp = new CacheDependency(controllerContext.HttpContext.Server.MapPath("/Data/123.txt")); 53 if (flag) 54 { 55 56 57 //cache[TempDataSessionStateKey] = values; 58 cache.Insert(TempDataSessionStateKey, values, dp); 59 60 return; 61 } 62 cache.Insert(TempDataSessionStateKey, values, dp); 63 //if (cache[TempDataSessionStateKey] != null) 64 //{ 65 // cache.Remove(TempDataSessionStateKey); 66 //} 67 } 68 } 69 } 70 }
把TempData的值存入到NoSQL Memcached中实现真正的分布式
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using Memcached.ClientLibrary; 6 7 namespace WebDemo.Models 8 { 9 public static class MemcacheHelper 10 { 11 private static MemcachedClient mc; 12 13 static MemcacheHelper() 14 { 15 //通过客户端来进行memcached的集群配置,在插入数据的时候,使用一致性哈希算法,将对应的value值存入Memcached 16 String[] serverlist = { "127.0.0.1:11211" }; 17 18 // 初始化Memcached的服务池 19 SockIOPool pool = SockIOPool.GetInstance("test"); 20 //设置服务器列表 21 pool.SetServers(serverlist); 22 //各服务器之间负载均衡的设置比例 23 pool.SetWeights(new int[] { 1 }); 24 pool.Initialize(); 25 //创建一个Memcached的客户端对象 26 mc = new MemcachedClient(); 27 mc.PoolName = "test"; 28 //是否启用压缩数据:如果启用了压缩,数据压缩长于门槛的数据将被储存在压缩的形式 29 mc.EnableCompression = false; 30 31 } 32 /// <summary> 33 /// 插入值 34 /// </summary> 35 /// <param name="key">建</param> 36 /// <param name="value">值</param> 37 /// <param name="expiry">过期时间</param> 38 /// <returns></returns> 39 public static bool Set(string key, object value,DateTime expiry){ 40 return mc.Set(key, value, expiry); 41 } 42 /// <summary> 43 /// 获取值 44 /// </summary> 45 /// <param name="key"></param> 46 /// <returns></returns> 47 public static object Get(string key) 48 { 49 return mc.Get(key); 50 } 51 } 52 }
自定义的我们的DataProvider:
1 public class CrossSessionTempData2 : ITempDataProvider 2 { 3 4 internal const string TempDataSessionStateKey = "__ControllerTempData"; 5 6 public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext) 7 { 8 9 Dictionary<string, object> dictionary = MemCaheHelper.Get(TempDataSessionStateKey) as Dictionary<string, object>; 10 if (dictionary != null) 11 { 12 MemCaheHelper.Set(TempDataSessionStateKey, dictionary, DateTime.MinValue); 13 return dictionary; 14 } 15 return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); 16 } 17 18 public virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values) 19 { 20 if (controllerContext == null) 21 { 22 throw new ArgumentNullException("controllerContext"); 23 } 24 25 bool flag = values != null && values.Count > 0; 26 if (flag) 27 { 28 29 MemCaheHelper.Set(TempDataSessionStateKey, values,DateTime.Now.AddMinutes(1)); 30 return; 31 } 32 33 if (MemCaheHelper.Get(TempDataSessionStateKey) != null) 34 { 35 MemCaheHelper.Set(TempDataSessionStateKey,values,DateTime.MinValue); 36 } 37 38 39 } 40 }