加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net mvc 之旅 —— 第五站 从源码中分析asp.net mvc 中的Te

发布时间:2020-12-16 08:46:54 所属栏目:asp.Net 来源:网络整理
导读:在mvc的controller中,我们知道有很多的临时变量存放数据,比如说viewData,viewBag,还有一个比较特殊的tempData,关于前两个或许大家都明白, 基本上是一个东西,就是各自的编程写法不一样,最终都会放到viewContext中,然后送到WebPage中,如果你要证明的

  在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等等。。。

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

?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读