asp.net mvc 之旅 —— 第五站 从源码中分析asp.net mvc 中的Te
在mvc的controller中,我们知道有很多的临时变量存放数据,比如说viewData,viewBag,还有一个比较特殊的tempData,关于前两个或许大家都明白, 基本上是一个东西,就是各自的编程写法不一样,最终都会放到viewContext中,然后送到WebPage中,如果你要证明的话,可以看下下面的代码。 /// <summary>Gets the dynamic view data dictionary.</summary> <returns>The dynamic view data dictionary.</returns> [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.</returns> public ViewDataDictionary ViewData { this._viewDataDictionary == ) { this._viewDataDictionary = new ViewDataDictionary(); } ._viewDataDictionary; } setthis._viewDataDictionary = value; } } 从上面的代码中可以看到,其实ViewBag就是获取ViewData的数据,对不对。。。 ? 一:TempData ? ? 至于这个东西怎么用,大家貌似都记得是可访问一次后即刻消失,好像貌似也就这样了,当然不知道有没有人对tempdata的底层代码进行研究呢??? 看一下它的底层到底是怎么来实现的。 ? 1. TempData源代码 ? ? 首先我们看一下TempData的类型是TempDataDictionary,可以看到这个类型肯定是实现了IDictionary接口的自定义字典, TempDataDictionary TempData { this.ControllerContext != null && .ControllerContext.IsChildAction) { .ControllerContext.ParentActionViewContext.TempData; } this._tempDataDictionary == this._tempDataDictionary = new TempDataDictionary(); } ._tempDataDictionary; } this._tempDataDictionary = value; } } 从上面代码可以看到,tempdate默认是new了一个TempDataDictionary类,这个类中很好玩的地方在于这里有一个load方法,这个load方法就是获取真 正的provider,比如下面这样: Loads the specified controller context by using the specified data provider.<param name="controllerContext">The controller context.</param> <param name="tempDataProvider">The temporary data provider.</param> public void Load(ControllerContext controllerContext,ITempDataProvider tempDataProvider) { IDictionary<string,object> dictionary = tempDataProvider.LoadTempData(controllerContext); this._data = ((dictionary != null) ? new Dictionary<object>(dictionary,StringComparer.OrdinalIgnoreCase) : object>(StringComparer.OrdinalIgnoreCase)); this._initialKeys = new HashSet<string>(._data.Keys,StringComparer.OrdinalIgnoreCase); ._retainedKeys.Clear(); } 这个load方法就是非常重要的,这里的参数ITempDataProvider就是我们在BeginExecute方法赋值的,继续往下看,不要着急哦。。。 ? 2.?BeginExecute ? ?我们知道,mvc框架其实是截获了mvcroutehandler来进行截获url的请求,继而将后续的处理就由mvc框架来接管,最终会执行到Controller类下面的 BeginExecute,如果你不信,我可以开心加愉快的给你上代码,比如下面这样: protected virtual IAsyncResult BeginExecute(RequestContext requestContext,AsyncCallback callback,1)">object state) { Action action2 = ; .DisableAsyncSupport) { if (action2 == ) { action2 = delegate { .Execute(requestContext); }; } Action action = action2; AsyncResultWrapper.BeginSynchronous(callback,state,action,_executeTag); } if (requestContext == ) { throw new ArgumentNullException("requestContext"); } base.VerifyExecuteCalledOnce(); .Initialize(requestContext); BeginInvokeDelegate<Controller> beginDelegate = (asyncCallback,callbackState,controller) => controller.BeginExecuteCore(asyncCallback,callbackState); EndInvokeVoidDelegate<Controller> endDelegate = delegate (IAsyncResult asyncResult,Controller controller) { controller.EndExecuteCore(asyncResult); }; return AsyncResultWrapper.Begin<Controller>(callback,beginDelegate,endDelegate,1)">this,_executeTag,-1,1)">); } 上面这段代码中,你一定要看清楚上面标红的地方,这里我们看到了,其实这里是一个异步的beginxxx,endxxx的操作,问题就是在这里,首先我们从 beginInvoke说起。 ? <1> beginDelegate ? ? ? ?这个异步操作中,我们可以看到,其实执行的是一个controller.BeginExecuteCore(asyncCallback,callbackState) 方法,对吧,然后我们可以 感兴趣的看一下这个方法干了什么? virtual IAsyncResult BeginExecuteCore(AsyncCallback callback,1)"> state) { IAsyncResult result; this.PossiblyLoadTempData(); try { Action action2 = ; string actionName = GetActionName(.RouteData); IActionInvoker invoker = .ActionInvoker; IAsyncActionInvoker invoker = invoker as IAsyncActionInvoker; if (invoker != ) { BeginInvokeDelegate<ExecuteCoreState> beginDelegate = (asyncCallback,asyncState,innerState) => innerState.AsyncInvoker.BeginInvokeAction(innerState.Controller.ControllerContext,innerState.ActionName,asyncCallback,asyncState); EndInvokeVoidDelegate<ExecuteCoreState> endDelegate = (IAsyncResult asyncResult,ExecuteCoreState innerState) { if (!innerState.AsyncInvoker.EndInvokeAction(asyncResult)) { innerState.Controller.HandleUnknownAction(innerState.ActionName); } }; ExecuteCoreState invokeState = ExecuteCoreState { Controller = ,AsyncInvoker = invoker,ActionName = actionName }; return AsyncResultWrapper.Begin<ExecuteCoreState>(callback,invokeState,_executeCoreTag,1)">); } if (!invoker.InvokeAction(.ControllerContext,actionName)) { .HandleUnknownAction(actionName); } }; } Action action = action2; result =catch.PossiblySaveTempData(); throw; } result; } 从上面的代码中,你应该看到了有一个?this.PossiblyLoadTempData()方法,看这个名字我们大概就可以猜得到这个方法和tempdate肯定有莫大的关系。 说时迟那时快,我们可以看下这个方法到底干了什么。。。在一系列跟踪之后,我们最后会到这个代码里面去了,如下所示: internal void PossiblyLoadTempData() { .ControllerContext.IsChildAction) { base.TempData.Load(base.ControllerContext,this.TempDataProvider); } } ? 请大家看清了,这里我们调用了刚才文章开头出说到的Tempdata.Load方法,那么问题来了,这里的TempDataProvider到底是怎么来的。我们继续来看代码: ITempDataProvider TempDataProvider { this._tempDataProvider == this._tempDataProvider = this.CreateTempDataProvider(); } ._tempDataProvider; } this._tempDataProvider = value; } } ? 看到没有,然后TempDataProvider然来是调用了CreateTempDataProvider方法来实现的,下一步我们来看一下CreateTempDataProvider到底干了什么。 virtual ITempDataProvider CreateTempDataProvider() { ITempDataProviderFactory service = this.Resolver.GetService<ITempDataProviderFactory>(); if (service != service.CreateInstance(); } return (this.Resolver.GetService<ITempDataProvider>() ?? SessionStateTempDataProvider()); } 从上面这个代码,我们应该就明白了,然来我们的tempdata默认是由SessionStateTempDataProvider来提供的,好了,接下来我们就可以继续看看 SessionStateTempDataProvider大概实现的业务逻辑。 class SessionStateTempDataProvider : ITempDataProvider { const string TempDataSessionStateKey = __ControllerTempData; virtual IDictionary< LoadTempData(ControllerContext controllerContext) { HttpSessionStateBase session = controllerContext.HttpContext.Session; if (session != ) { Dictionary<object> dictionary = session["] as Dictionary<if (dictionary != ) { session.Remove(); dictionary; } } (StringComparer.OrdinalIgnoreCase); } virtual void SaveTempData(ControllerContext controllerContext,IDictionary< values) { if (controllerContext == controllerContext); } HttpSessionStateBase session =bool flag = (values != null) && (values.Count > 0); if (session == if (flag) { InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled); } } else (flag) { session["] = values; } if (session["] != ) { session.Remove(); } } } 可以看到,SessionStateTempDataProvider 是实现了ITempDataProvider接口,里面有两个方法LoadTempData 和SaveTempData方法,而 LoadTempData方法的逻辑很奇葩,你可以仔细观察一下哦,如果 if (session != null)满足就清空字典的数据,否则就不清除,这个逻辑大概就向 你展示了为什么数据只能被读取一次,下次读取的时候,就走了这个if(session!=null)给清空了,你怎么可能再读取session中的数据呢。。。这个 就是为什么tempdata只能被读取一次的真相,是不是很好玩。 ? <2> EndExecuteCore ? ? 有人可能会问了,第二个方法SaveTempData是什么时候执行的,当然就是EndExecuteCore里面了,比如你看: protected virtual void EndExecuteCore(IAsyncResult asyncResult) { { AsyncResultWrapper.End(asyncResult,1)">finally { .PossiblySaveTempData(); } } 可以看到它的默认实现是session,当然你也可以实现一个自定义的provider,比如用cache来存放这个临时数据,或者是redis,mongodb等等。。。 当然还有更多有趣的东西等待你发掘哦~~~ ? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc – ASP.NET MVC:让API控制器操作同
- asp.net-mvc – ASP.NET Web API错误:未找到与请
- asp.net-mvc – 为什么我不能从Web平台安装程序安
- ASP.NET MVC URL在CSS文件中自动解析
- asp.net – 即使它已经过时,仍然可以使用System.
- 如果method参数是string或int,则ASP.NET WebAPI抛
- asp.net – 注册.NET 4.5 IIS 10 Windows 10
- asp.net-mvc – 在ASP.NET MVC ViewModel类中获取
- asp.net – 如何在.Net Core中间件中获取当前子域
- asp.net-mvc – jQuery脚本包含在mvc 4模板的页面