asp.Netmvcの旅-第5局はソースコードからaspを分析する.NetmvcでのTempData
26042 ワード
mvcのcontrollerでは、viewData、viewBag、さらに特殊なtempDataなど、多くの一時変数がデータを格納していることを知っています.
基本的には、それぞれのプログラミングの書き方が異なり、最終的にはviewContextに入れてWebPageに送り、証明するなら次のコードを見ることができます.
上のコードから、tempdateのデフォルトはnewのTempDataDictionaryクラスで、このクラスの中でとても面白いところはここにload方法があって、このload方法は真を取得することです
正のproviderは、次のようになります.
上のこのコードの中で、あなたはきっと上の赤い地方をはっきり見て、ここで私达は见て、実はここは1つの非同期のbeginxxxで、endxxxの操作で、问题はここで、まず私达は
beginInvokeが言う.
<1> beginDelegate
この非同期操作では、実際にはcontrollerが実行されていることがわかります.BeginExecuteCore(asyncCallback,callbackState)メソッドですね.
興味を持ってこの方法を見て何をしましたか?
上のコードから、thisが見えます.PossiblyLoadTempData()メソッドは、この名前を見るとtempdateと大きな関係があるに違いないと推測できます.
遅ればせながら、私たちはこの方法がいったい何をしたのかを見ることができます...一連の追跡の後、私たちは最後にこのコードの中に行きました.以下のようにします.
皆さん、よく見てください.ここではさっきの文章の冒頭で述べたTempdataを呼び出しました.Loadメソッド、では問題が来ましたが、ここのTempDataProviderはいったいどうやって来たのでしょうか.コードを見てみましょう
見たかどうか、それからTempDataProviderはCreateTempDataProviderメソッドを呼び出して実現したので、次にCreateTempDataProviderが何をしたのかを見てみましょう.
上のコードから、私たちは理解しているはずですが、私たちのtempdataのデフォルトはSessionStateTempDataProviderによって提供されています.はい、次は続けて見ることができます.
SessionStateTempDataProviderが概ね実現するビジネスロジック.
SessionStateTempDataProviderはITempDataProviderインタフェースを実現しており、LoadTempDataとSaveTempDataの2つの方法があり、
LoadTempDataメソッドの論理は奇抜で、よく観察してみてください.if(session!=null)が満足すれば辞書のデータをクリアします.そうしないとクリアしません.この論理はたぶん
なぜデータが一度しか読み取れないのかを示しましたが、次の読み込みの時に、このif(session!=null)を空にしてしまったのに、sessionのデータを再び読み取ることはできません.のこれ
なぜtempdataは一度しか読み取れないのか、面白いのではないでしょうか.
<2> EndExecuteCore
2つ目の方法SaveTempDataはいつ実行されたのかと聞かれるかもしれませんが、もちろんEndExecuteCoreの中にあります.例えば、見てください.
デフォルトの実装はsessionであることがわかります.もちろん、cacheでこの一時データを保存したり、redis、mongodbなどのカスタムproviderを実装したりすることもできます.の
もちろんもっと面白いものが発掘されるのを待っていますよ~~~
基本的には、それぞれのプログラミングの書き方が異なり、最終的にはviewContextに入れてWebPageに送り、証明するなら次のコードを見ることができます.
///
Gets the dynamic view data dictionary. ///
The dynamic view data dictionary. [Dynamic] public dynamic ViewBag { [return: Dynamic] get { if (this._dynamicViewDataDictionary == null) { this._dynamicViewDataDictionary = new DynamicViewDataDictionary(() => this.ViewData); } return this._dynamicViewDataDictionary; } } /// Gets or sets the dictionary for view data. ///
The dictionary for the view data. public ViewDataDictionary ViewData { get { if (this._viewDataDictionary == null) { this._viewDataDictionary = new ViewDataDictionary(); } return this._viewDataDictionary; } set { this._viewDataDictionary = value; } }
从上面的代码中可以看到,其实ViewBag就是获取ViewData的数据,对不对。。。
一:TempData
至于这个东西怎么用,大家貌似都记得是可访问一次后即刻消失,好像貌似也就这样了,当然不知道有没有人对tempdata的底层代码进行研究呢???
看一下它的底层到底是怎么来实现的。
1. TempData源代码
首先我们看一下TempData的类型是TempDataDictionary,可以看到这个类型肯定是实现了IDictionary接口的自定义字典,
public TempDataDictionary TempData
{
get
{
if (this.ControllerContext != null && this.ControllerContext.IsChildAction)
{
return this.ControllerContext.ParentActionViewContext.TempData;
}
if (this._tempDataDictionary == null)
{
this._tempDataDictionary = new TempDataDictionary();
}
return this._tempDataDictionary;
}
set
{
this._tempDataDictionary = value;
}
}
上のコードから、tempdateのデフォルトはnewのTempDataDictionaryクラスで、このクラスの中でとても面白いところはここにload方法があって、このload方法は真を取得することです
正のproviderは、次のようになります.
///
Loads the specified controller context by using the specified data provider. /// The controller context. /// The temporary data provider. public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider) { IDictionary<string, object> dictionary = tempDataProvider.LoadTempData(controllerContext); this._data = ((dictionary != null) ? new Dictionary<string, object>(dictionary, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)); this._initialKeys = new HashSet<string>(this._data.Keys, StringComparer.OrdinalIgnoreCase); this._retainedKeys.Clear(); }
这个load方法就是非常重要的,这里的参数ITempDataProvider就是我们在BeginExecute方法赋值的,继续往下看,不要着急哦。。。
2. BeginExecute
我们知道,mvc框架其实是截获了mvcroutehandler来进行截获url的请求,继而将后续的处理就由mvc框架来接管,最终会执行到Controller类下面的
BeginExecute,如果你不信,我可以开心加愉快的给你上代码,比如下面这样:
protected virtual IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state)
{
Action action2 = null;
if (this.DisableAsyncSupport)
{
if (action2 == null)
{
action2 = delegate {
this.Execute(requestContext);
};
}
Action action = action2;
return AsyncResultWrapper.BeginSynchronous(callback, state, action, _executeTag);
}
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
base.VerifyExecuteCalledOnce();
this.Initialize(requestContext);
BeginInvokeDelegate beginDelegate = (asyncCallback, callbackState, controller) => controller.BeginExecuteCore(asyncCallback, callbackState);
EndInvokeVoidDelegate endDelegate = delegate (IAsyncResult asyncResult, Controller controller) {
controller.EndExecuteCore(asyncResult);
};
return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, this, _executeTag, -1, null);
}
上のこのコードの中で、あなたはきっと上の赤い地方をはっきり見て、ここで私达は见て、実はここは1つの非同期のbeginxxxで、endxxxの操作で、问题はここで、まず私达は
beginInvokeが言う.
<1> beginDelegate
この非同期操作では、実際にはcontrollerが実行されていることがわかります.BeginExecuteCore(asyncCallback,callbackState)メソッドですね.
興味を持ってこの方法を見て何をしましたか?
protected virtual IAsyncResult BeginExecuteCore(AsyncCallback callback, object state)
{
IAsyncResult result;
this.PossiblyLoadTempData();
try
{
Action action2 = null;
string actionName = GetActionName(this.RouteData);
IActionInvoker invoker = this.ActionInvoker;
IAsyncActionInvoker invoker = invoker as IAsyncActionInvoker;
if (invoker != null)
{
BeginInvokeDelegate beginDelegate = (asyncCallback, asyncState, innerState) => innerState.AsyncInvoker.BeginInvokeAction(innerState.Controller.ControllerContext, innerState.ActionName, asyncCallback, asyncState);
EndInvokeVoidDelegate endDelegate = delegate (IAsyncResult asyncResult, ExecuteCoreState innerState) {
if (!innerState.AsyncInvoker.EndInvokeAction(asyncResult))
{
innerState.Controller.HandleUnknownAction(innerState.ActionName);
}
};
ExecuteCoreState invokeState = new ExecuteCoreState {
Controller = this,
AsyncInvoker = invoker,
ActionName = actionName
};
return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, invokeState, _executeCoreTag, -1, null);
}
if (action2 == null)
{
action2 = delegate {
if (!invoker.InvokeAction(this.ControllerContext, actionName))
{
this.HandleUnknownAction(actionName);
}
};
}
Action action = action2;
result = AsyncResultWrapper.BeginSynchronous(callback, state, action, _executeCoreTag);
}
catch
{
this.PossiblySaveTempData();
throw;
}
return result;
}
上のコードから、thisが見えます.PossiblyLoadTempData()メソッドは、この名前を見るとtempdateと大きな関係があるに違いないと推測できます.
遅ればせながら、私たちはこの方法がいったい何をしたのかを見ることができます...一連の追跡の後、私たちは最後にこのコードの中に行きました.以下のようにします.
internal void PossiblyLoadTempData()
{
if (!base.ControllerContext.IsChildAction)
{
base.TempData.Load(base.ControllerContext, this.TempDataProvider);
}
}
皆さん、よく見てください.ここではさっきの文章の冒頭で述べたTempdataを呼び出しました.Loadメソッド、では問題が来ましたが、ここのTempDataProviderはいったいどうやって来たのでしょうか.コードを見てみましょう
public ITempDataProvider TempDataProvider
{
get
{
if (this._tempDataProvider == null)
{
this._tempDataProvider = this.CreateTempDataProvider();
}
return this._tempDataProvider;
}
set
{
this._tempDataProvider = value;
}
}
見たかどうか、それからTempDataProviderはCreateTempDataProviderメソッドを呼び出して実現したので、次にCreateTempDataProviderが何をしたのかを見てみましょう.
protected virtual ITempDataProvider CreateTempDataProvider()
{
ITempDataProviderFactory service = this.Resolver.GetService();
if (service != null)
{
return service.CreateInstance();
}
return (this.Resolver.GetService() ?? new SessionStateTempDataProvider());
}
上のコードから、私たちは理解しているはずですが、私たちのtempdataのデフォルトはSessionStateTempDataProviderによって提供されています.はい、次は続けて見ることができます.
SessionStateTempDataProviderが概ね実現するビジネスロジック.
public class SessionStateTempDataProvider : ITempDataProvider
{
internal const string TempDataSessionStateKey = "__ControllerTempData";
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.Remove("__ControllerTempData");
return dictionary;
}
}
return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
}
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");
}
}
}
SessionStateTempDataProviderはITempDataProviderインタフェースを実現しており、LoadTempDataとSaveTempDataの2つの方法があり、
LoadTempDataメソッドの論理は奇抜で、よく観察してみてください.if(session!=null)が満足すれば辞書のデータをクリアします.そうしないとクリアしません.この論理はたぶん
なぜデータが一度しか読み取れないのかを示しましたが、次の読み込みの時に、このif(session!=null)を空にしてしまったのに、sessionのデータを再び読み取ることはできません.のこれ
なぜtempdataは一度しか読み取れないのか、面白いのではないでしょうか.
<2> EndExecuteCore
2つ目の方法SaveTempDataはいつ実行されたのかと聞かれるかもしれませんが、もちろんEndExecuteCoreの中にあります.例えば、見てください.
protected virtual void EndExecuteCore(IAsyncResult asyncResult)
{
try
{
AsyncResultWrapper.End(asyncResult, _executeCoreTag);
}
finally
{
this.PossiblySaveTempData();
}
}
デフォルトの実装はsessionであることがわかります.もちろん、cacheでこの一時データを保存したり、redis、mongodbなどのカスタムproviderを実装したりすることもできます.の
もちろんもっと面白いものが発掘されるのを待っていますよ~~~