本篇主要討論ASP NET MVC中TempData是如何實現的 通過研讀MVC的源代碼妳將清楚的了解MVC是如何實現TempData功能的
TempData特性
TempDataDictionary與ITempDataProvider
TempDataDictionary的設計
SessionStateTempDataProvider與ITempDataProvider
TempData特性Top
TempData的特性就是可以在兩個Action之間傳遞數據 它會保存壹份數據到下壹個Action 並隨著再下壹個Action的到來而失效 所以它被用在兩個Action之間來保存數據 比如 這樣壹個場景 妳的壹個Action接受壹些post的數據 然後交給另壹個Action來處理 並顯示到頁面 這時就可以使用TempData來傳遞這份數據
那到底TempData是怎樣完成這個功能的呢?下面我們從MVC的源代碼入手來解析TempData的機制
TempDataDictionary與ITempDataProviderTop
首先來看看ITempDataProvider接口 從字面意思上看我們先把它翻譯為 暫時數據的提供者所遵從的規則 它約定了兩個方法
public interface ITempDataProvider
{
IDictionary LoadTempData(ControllerContext controllerContext);
void SaveTempData(ControllerContext controllerContext IDictionary values);
}
這兩個方法是LoadTempData和SaveTempData 我們猜想這兩個方法是用來取得TempData容器和保存TempData數據的 因為LoadTempData返回壹個IDictionary類型 而SaveTempData沒有返回類型 而參數ControllerContext就是針對不同的用戶上下文來設計的 標明是對那壹個上下文的TempData進行操作 的確是這樣的 後面會驗證我們的猜想
再來看看TempDataDictionary 我們對這個類的第壹印象在哪裏呢?是在ControllerBase類中的TempData屬性 在普通的Controller中我們打上tempdata vs幫助我們完成的那個屬性其實就是ControllerBase類中的TempData 因此我們明白了 不管是在controller中 還是在view中 所有對TempData的操作都是對TempDataDictionary類型的操作 那ITempDataProvider有是怎麽與TempDataDictionary聯系的呢?看壹下TempDataDictionary的設計便壹目了然
TempDataDictionary的設計Top
public class TempDataDictionary : IDictionary<string object> ISerializable
這是TempDataDictionary的簽名 我們看到它繼承了壹個IDictionary<string object>的字典類型和壹個ISerializable的接口 因此我們知道它是可以被序列化和反序列化的 該類有壹個常字符串類型的字段和壹個Dictionary<string object>類型的字段
internal const string _tempDataSerializationKey = __tempData ;? internal Dictionary<string object> _data;
在它帶參的構造函數中發現了對_tempDataSerializationKey的使用
protected TempDataDictionary(SerializationInfo info StreamingContext context)
{
_initialKeys = new HashSet<string>(StringComparer OrdinalIgnoreCase);
_modifiedKeys = new HashSet<string>(StringComparer OrdinalIgnoreCase);
_data = info GetValue(_tempDataSerializationKey typeof(Dictionary<string object>))
as Dictionary<string object>;
}
我們可以看到這是用來從壹個流中 反序列化得到壹個Dictionary類型的過程
另壹點 在controller中 我們可以這樣使用TempData的
TempData[ msg ] = new Object();? Object obj = TempData[ msg ] as object; 在了解它的索引器之前我們先看看它的幾個字段和方法 TempDataDictionary類重要的字段有三個
internal Dictionary<string object> _data;? private HashSet<string> _initialKeys;? private HashSet<string> _modifiedKeys; _data用來存放真正的數據 _initialKeys用來存放原先數據的key _modifiedKeys用來存放修改過或新添加的數據key 為什麽要這樣呢?回想壹下TempData的特性 TempData只存放壹次數據 到第三個Action時 第壹個Action存放的數據就失效了 所以 _initialKeys被設計來存放那些數據是原來的 _modifiedKeys被設計來存放那些數據是修改過的或是新添加上的 這樣就區分了 舊 數據和 新 數據 那下壹步就是把 舊 的刪除 把 新 的記錄了
我們再到索引器看看 因為我們對TempData的操作是從索引器開始的 下面是索引器的代碼
public object this[string key]
{
get
{
object value;
if (TryGetValue(key out value))
{
return value;
}
return null;
}
set {
_data[key] = value;
_modifiedKeys Add(key);
}
}
當我們TempData[ msg ]=new Object();時不僅向_data中添加了數據 同時_modifiedKeys也保存了 新 數據的key 那什麽時候 新 數據被保存 舊 數據被刪除 真正的執行呢?這個過程是在Load和Save方法中發生的 下面看它們的具體實現
public void Load(ControllerContext controllerContext ITempDataProvider tempDataProvider)
{
IDictionary<string object> providerDictionary = tempDataProvider LoadTempData(? controllerContext);? _data = (providerDictionary != null) ? new Dictionary<string object>(providerDictionary
StringComparer OrdinalIgnoreCase) : new Dictionary<string object>
(StringComparer OrdinalIgnoreCase);
_initialKeys = new HashSet<string>(_data Keys);
_modifiedKeys Clear();
}
public void Save(ControllerContext controllerContext ITempDataProvider tempDataProvider)
{
if (_modifiedKeys Count > )
{
// Apply change tracking
foreach (string x in _initialKeys)
{
if (!_modifiedKeys Contains(x))
{
_data Remove(x);
}
}
// Store the dictionary
tempDataProvider SaveTempData(controllerContext _data);
}
}
我們看到TempDataDictionary的Load方法首先是調用了ITempDataProvider的LoadTempData方法來獲取tempdata容器 然後讓_initialKeys等於_data Keys 相當於保存了 舊 數據的key 然後清空_modifiedKeys 相當於目前沒有 新 數據 而Save方法則是檢查_modifiedKeys Count是否大於 就相當於檢查是否有 新 數據 有則調用ITempDataProveder的SaveTempData方法保存掉 新 數據 這裏也驗證了我們先前的猜想是正確的
說到這裏 我們似乎還沒有發現沒有壹個地方調用TempDataDictionary的Load和Save方法 也就是說 新 舊 數據壹直在都在_data中 似乎 舊 的數據沒有真正刪除 新 數據也壹直沒有壹個安定的家
我們說對TempData中數據的 刷新 操作(刷新操作即把 舊 數據刪除 把 新 數據保存)應該發生在執行Action的時候 那在什麽地方我們執行了Action呢 是在IController的Execute方法中 IController<=ControllerBase<=Controller 順著這樣的繼承順序 我們找到Controller類的ExecuteCore方法 這裏是執行Action的地方 下面我們看看ExecuteCore方法的實現
protected override void ExecuteCore()
{
TempData Load(ControllerContext TempDataProvider);
try {
string actionName = RouteData GetRequiredString( action );
if (!ActionInvoker InvokeAction(ControllerContext actionName))
{? HandleUnknownAction(actionName);
}
}
finally
{
TempData Save(ControllerContext TempDataProvider);
}
}
我們看到在這裏 Action執行之前TempData Load Action執行之後TempData Save 這就實現了TempData的 刷新 操作
SessionStateTempDataProvider與ITempDataProviderTop
到這裏 我們發現似乎還不知道到底數據是怎麽被保存的 我們只知道ITempDataProvider提供了壹個保存數據和獲取容器的這麽壹個約定 那麽具體的實現肯定是繼承了ITempDataProvider接口的類來做 SessionStateTempDataProvider就是這麽壹個類
我們知道是在Controller類中的ExecuteCore方法中執行了 刷新 操作 我們還知道TempDataDictionary的Load和Save方法需要壹個ITempDataProvider的方法 那麽我們可以推斷肯定要去Controller類中尋找ITempDataProvider的實現 如我們所料
public ITempDataProvider TempDataProvider {
get {
if (_tempDataProvider == null)
{
_tempDataProvider = new SessionStateTempDataProvider();
}
return _tempDataProvider;
}
set {
_tempDataProvider = value;
}
}
這裏使用了屬性註入 強硬的註入了壹個SessionStateTempDataProvider對象 那麽具體是怎樣實現存儲的就要去看壹下SessionStateTempDataProvider類了
SessionStateTempDataProvider有壹個常字符串字段
internal const string TempDataSessionStateKey = __ControllerTempData ; 下面是LoadTempData方法
public virtual IDictionary LoadTempData(ControllerContext controllerContext)
{
HttpContextBase ;
if ( == null)
{
throw new InvalidOperationException(
MVCResources SessionStateTempDataProvider_SessionStateDisabled);
}
Dictionary<string object> tempDataDictionary = [TempDataSessionStateKey]? as Dictionary<string object>;
if (tempDataDictionary != null)
{
// If we got it from Session remove it so that no other request gets it
(TempDataSessionStateKey);
return tempDataDictionary;
}
else
{
return new Dictionary<string object>(StringComparer OrdinalIgnoreCase);
}
}
上面的代碼很簡單 原來它把Dictionary類型的數據存進了Session[ __ControllerTempData ]裏 讀的時候也只是簡單的類型轉換壹下就返回了
下面是SaveTempData方法
public virtual void SaveTempData(ControllerContext controllerContext IDictionary values)
{
HttpContextBase ;
if ( == null)
{
throw new InvalidOperationException(
MVCResources SessionStateTempDataProvider_SessionStateDisabled);
}
[TempDataSessionStateKey] = values;
} SaveTempData方法也很簡單
總結Top
lishixinzhi/Article/program/net/201311/13479