Asp.net mvc journey-the fifth station analyzes TempData and mvctempdata in asp.net mvc from the source code

Source: Internet
Author: User

Asp.net mvc journey-the fifth station analyzes TempData and mvctempdata in asp.net mvc from the source code

In the controller of mvc, we know that there are many temporary variables that store data, such as viewData and viewBag. There is also a special tempData. you may understand the first two,

Basically, the programming method is different. They will be put in viewContext and sent to WebPage. If you want to prove it, you can refer to the following code.

        /// <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;            }        }

From the code above, we can see that ViewBag is actually used to get ViewData, right...

 

I. TempData

As for how to use this item, we all seem to remember that it disappears immediately after one access, as if it was like this. Of course, I don't know if anyone has studied the underlying code of tempdata ???

Let's see how the underlying layer is implemented.

 

1. Source Code of TempData

First, let's take a look at the TempData type as TempDataDictionary. We can see that this type must be a custom dictionary that implements the IDictionary interface,

        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;            }        }

From the code above, we can see that the default value of tempdate is a new TempDataDictionary class. The interesting part of this class is that there is a load method, and this load method is used to obtain the truth.

Is the provider, such as the following:

        /// <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();        }

This load method is very important. Here the parameter ITempDataProvider is assigned a value in the BeginExecute method. Continue to look down and don't worry...

 

2. BeginExecute

We know that the mvc framework actually intercepts mvcroutehandler to intercept url requests, and then takes over the subsequent processing by the mvc Framework, which will eventually be executed under the Controller class.

BeginExecute. If you don't believe it, I can happily add code to you, for example, the following:

        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);        }

In the above Code, you must have a clear view of the above marked red, here we see, in fact, this is an asynchronous beginxxx, endxxx operation, the problem is here, first of all from

Start with beginInvoke.

 

<1> beginDelegate

In this asynchronous operation, we can see that a controller. BeginExecuteCore (asyncCallback, callbackState) method is actually executed, right, then we can

If you are interested, let's take a look at what this method has done?

        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;        }

From the code above, you should see that there is a this. PossiblyLoadTempData () method. Looking at this name, we can probably guess that this method has a great relationship with tempdate.

Let's talk about it later. Let's take a look at what this method has done... After a series of traces, we will finally go to the code, as shown below:

        internal void PossiblyLoadTempData()        {            if (!base.ControllerContext.IsChildAction)            {                base.TempData.Load(base.ControllerContext, this.TempDataProvider);            }        }

 

You can see it clearly. Here we call the Tempdata. Load method mentioned at the beginning of the article. The question is, how does the TempDataProvider come from here. Let's continue to look at the Code:

        public ITempDataProvider TempDataProvider        {            get            {                if (this._tempDataProvider == null)                {                    this._tempDataProvider = this.CreateTempDataProvider();                }                return this._tempDataProvider;            }            set            {                this._tempDataProvider = value;            }        }

 

See no. Then, TempDataProvider calls the CreateTempDataProvider Method for implementation. Next, let's take a look at what CreateTempDataProvider has done.

        protected virtual ITempDataProvider CreateTempDataProvider()        {            ITempDataProviderFactory service = this.Resolver.GetService<ITempDataProviderFactory>();            if (service != null)            {                return service.CreateInstance();            }            return (this.Resolver.GetService<ITempDataProvider>() ?? new SessionStateTempDataProvider());        }

From the above Code, we should understand that our tempdata is provided by SessionStateTempDataProvider by default. Now, let's continue to look at it.

SessionStateTempDataProvider roughly implements the business logic.

  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");            }        }    }

As you can see, SessionStateTempDataProvider implements the ITempDataProvider interface, which has two methods: LoadTempData and SaveTempData.

The logic of the LoadTempData method is amazing. You can take a closer look, if (session! = Null) Clear the data in the dictionary if it meets the requirements. Otherwise, the data is not cleared.

You show why the data can only be read once. The next time you read the data, you go through this if (session! = Null). How can you read data from the session... This

That is why tempdata can only be read once. Is it fun.

 

<2> EndExecuteCore

Someone may ask, when is the second method SaveTempData executed? Of course, it is in EndExecuteCore. For example, you can see:

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

We can see that its default implementation is session. Of course, you can also implement a custom provider, such as using cache to store this temporary data, or redis, mongodb, and so on...

Of course, there are more interesting things to explore ~~~

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.