文章出處

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

當然還有更多有趣的東西等待你發掘哦~~~

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()