首页 > 编程语言 >asp.net mvc 之旅 —— 第五站 从源码中分析asp.net mvc 中的TempData

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

时间:2023-04-18 12:41:20浏览次数:45  
标签:session asp return mvc ._ ControllerContext new net null

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

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

 

标签:session,asp,return,mvc,._,ControllerContext,new,net,null
From: https://blog.51cto.com/u_15353947/6202795

相关文章

  • asp.net signalR 专题—— 第三篇 如何从外部线程访问 PersistentConnection
       在前面的两篇文章中,我们讲到的都是如何将消息从server推向client,又或者是client再推向server,貌似这样的逻辑没什么异常,但是放在真实的环境中,你会很快发现有一个新需求,如何根据第三方系统的数据变化来即时的将新数据推送到各个客户端,比如下面这样:ok,原理就是上面的这......
  • asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析
       这篇文章我们开始看一下ActionFilter,从名字上其实就大概知道ActionFilter就是Action上的Filter,对吧,那么Action上的Filter大概有几个呢???这个问题其实还是蛮简单的,因为我们听说Mvc本身就是一个扩展性极强的框架,自然就是层层有拦截,层层有过滤,对吧,比如我们看到的如下Control......
  • 论 java.net.SocketException: sendto failed: EPIPE (Broken pipe) 的解决办法
    这里只是针对我昨天遇到的问题(上传文件过大,导致出现Socket异常)的解决办法。众所周知,tomcat是有默认的文件传输大小限制的(跟android前端),后来跟服务器的哥们协调了一下,他那边改成多少都不管用,这是其一。其二是后来查google得知,tomcat设置服务器的超时时间,后来还是一样,设置成多少都不......
  • 基于DotNetCoreNPOI封装特性通用导出excel
    基于DotNetCoreNPOI封装特性通用导出excel目前根据项目中的要求,支持列名定义,列索引排序,行合并单元格,EXCEL单元格的格式也是随着数据的类型做对应的调整。效果图:调用方式可以看到时非常容易的能够导出数据,实际调用可能就三四句话//你的需要导出的数据集合,这里的......
  • pg事务篇(一)—— 事务与多版本并发控制MVCC
    一、MVCC常用实现方法一般MVCC有2种实现方法:写新数据时,把旧数据快照存入其他位置(如oracle的回滚段、sqlserver的tempdb)。当读数据时,读的是快照的旧数据。写新数据时,旧数据不删除,直接插入新数据。PostgreSQL就是使用的这种实现方法。1.PostgreSQL的MVCC实现方式优缺点优点无论事务......
  • 《Ubuntu — NetworkManager开机提示A start job is running for Network Manager wai
    轉自:https://www.cnblogs.com/zhuangquan/p/13209758.html,僅供參考學習使用1.NetworkManagerUbuntuServer:Ubuntu的Server版本只有终端界面,没有桌面GUI,且Server版本不会安装NetworkManager,所以UbuntuServer网络由配置文件进行配置。由于Server版本一般用作服务器的......
  • ASP.NET Core设置URLs的几种方法,完美解决.NET 6项目局域网IP地址远程无法访问的问题
    近期在dotnet项目中遇到这样的问题:.net6运行以后无法通过局域网IP地址远程访问。后查阅官方文档。整理出解决问题的五种方式方法,通过新建一个新的WebApi项目演示如下:说明操作系统:Ubuntu22.04.2运行时:.NET6开发工具:VisualStudio2202新建webapi#只需要以下名利即可创......
  • asp.net core系列 26 EF模型配置(实体关系)
    一.概述EF实体关系定义了两个实体互相关联起来(主体实体和依赖实体的关系,对应数据库中主表和子表关系)。 在关系型数据库中,这种表示是通过外键约束来体现。本篇主要讲一对多的关系。先了解下描述关系的术语。(1)依赖实体: 这是包含外键属性的实体(子表)。有时称为ch......
  • Kubernetes 集群 Pod 资源启动命令(六)
    启动命令编写配置文件创建pod_command.yaml文件,并编写如下内容,即在容器启动之后,向、opt/text.txt文件写入时间戳,执行命令主要通过command字段传入,类型为列表格式#编写yamlapiVersion:v1kind:Namespacemetadata:name:dev---apiVersion:v1kind:Podmetadata:......
  • QT MVC开发模式
    一、简单介绍今天我来记录一下在Qt中使用MVC模式进行开发的过程。MVC(Model-View-Controller)是一种常见的软件架构模式,用于将应用程序的逻辑和用户界面分离开来。在Qt中,使用MVC模式可以大大提高应用程序的可维护性和可扩展性。通过将应用程序的逻辑和用户界面分离开来,可以更轻松地......