继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

从源码中分析asp.net mvc 中的TempData

大吉大利今晚学习
关注TA
已关注
手记 265
粉丝 36
获赞 142

  在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;            }        }        /// <summary>Gets or sets the dictionary for view data.</summary>        /// <returns>The dictionary for the view data.</returns>        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,比如下面这样:

复制代码

        /// <summary>Loads the specified controller context by using the specified data provider.</summary>        /// <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<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<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, state, beginDelegate, endDelegate, this, _executeTag, -1, null);        }

复制代码

上面这段代码中,你一定要看清楚上面标红的地方,这里我们看到了,其实这里是一个异步的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<ExecuteCoreState> beginDelegate = (asyncCallback, asyncState, innerState) => innerState.AsyncInvoker.BeginInvokeAction(innerState.Controller.ControllerContext, innerState.ActionName, asyncCallback, asyncState);                    EndInvokeVoidDelegate<ExecuteCoreState> 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<ExecuteCoreState>(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<ITempDataProviderFactory>();            if (service != null)            {                return service.CreateInstance();            }            return (this.Resolver.GetService<ITempDataProvider>() ?? 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方法,而

LoadTempData方法的逻辑很奇葩,你可以仔细观察一下哦,如果 if (session != null)满足就清空字典的数据,否则就不清除,这个逻辑大概就向

你展示了为什么数据只能被读取一次,下次读取的时候,就走了这个if(session!=null)给清空了,你怎么可能再读取session中的数据呢。。。这个

就是为什么tempdata只能被读取一次的真相,是不是很好玩。

 

<2> EndExecuteCore

    有人可能会问了,第二个方法SaveTempData是什么时候执行的,当然就是EndExecuteCore里面了,比如你看:

复制代码

        protected virtual void EndExecuteCore(IAsyncResult asyncResult)        {            try            {                AsyncResultWrapper.End(asyncResult, _executeCoreTag);            }            finally            {                this.PossiblySaveTempData();            }        }

复制代码

可以看到它的默认实现是session,当然你也可以实现一个自定义的provider,比如用cache来存放这个临时数据,或者是redis,mongodb等等。。。

当然还有更多有趣的东西等待你发掘哦~~~

 

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP