自定義TempData跨平臺思路
一:TempData的自定義實現。。。
TempData是用Session實現的,既然是Session,那模式是線程方式。。。這樣的Session是沒法進行跨平臺的。。。
那麽這就涉及到如何在多臺機器上部署、存儲...
- 關系型數據庫: sqlserver/mysql
- nosql: mongodb,redis。
問題1:重點在替換,不在實現。。。。。怎麽替換,以及使用方式
TempData繼承關系:Tempdata => TempDataDictionary=> SessionStateTempDataProvider=>ITempDataProvider
1 namespaceSystem.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 }
自定義TempData跨平臺思路