白话学习MVC(七)Action的执行一

1、概述

  在此系列开篇的时候介绍了MVC的生命周期 , 对于请求的处理,都是将相应的类的方法注册到HttpApplication事件中,经过事件的依次执行从而完成对请求的处理。对于MVC来讲,请求是先 通过路由系统,而后由一个MvcHandler来处理的,当请求到来时,执行此MvcHandler的ProcessRequest方法(由于已将 MvcHandler类的ProcessRequest方法注册到HttpApplication的事件中,因此事件的执行就触发了此方法)。详细请看以前介绍MVC生命周期的两篇博客
  下面咱们就以MVC声明周期为主线,来分析下MVC源码
html

public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
{
    protected virtual void ProcessRequest(HttpContext httpContext)
    {
        //使用HttpContextWrapper对HttpContext进行封装,封装的目的是为了解耦以得到可测试性.而后从RequestContext.RouteData中提取Controller名称.
        HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
        this.ProcessRequest(httpContext2);
    }
    
    protected internal virtual void ProcessRequest(HttpContextBase httpContext)
    {
        IController controller;
        IControllerFactory controllerFactory;
        this.ProcessRequestInit(httpContext, out controller, out controllerFactory);//获取到Controler和ControllerFactory实例,并赋值给局部变量
        try
        {
          //Action的调用、View的呈现
                controller.Execute(this.RequestContext);
                
        }
        finally
        {
            //释放当前Controler对象
            controllerFactory.ReleaseController(controller); 
        }
    }
}

  MVC中Action的调用,就是经过调用Contrller对象的Execute方法触发执行的!这个Controller对象是Controller激活的产物,Controller激活请参考上一篇博客。数组

2、Action的调用

  咱们知道Action的执行就是调用经过Controller激活获得的Controller对象的Execute方法,这个Controller对象就是咱们建立的Controller(例如:HomeController)类的实例,而咱们建立的HomeController等控制器都继承自Controller类、Controller抽象类继承ControllerBase抽象类、ControllerBase抽象类实现了IController接口。继承和实现关系为:
缓存

  咱们建立的控制器经过Controller的激活建立了实例,而后执行该实例的Execute方法,Execute方法定义在接口IController中,实如今类ControllerBase中,而该Excute方法内又调用ControllerBase类的抽象方法ExecuteCore,抽象方法ExecuteCore又在Controller类中实现。因此Action调用的流程为:先执行ControllerBase类的Execute方法,再执行Controller类的ExcuteCore方法。因此执行过程为:【ControllerBase类中的Execute方法】-->【Controller类中的ExecuteCore方法】服务器

namespace System.Web.Mvc
{
    public interface IController
    {
        void Execute(RequestContext requestContext);
    }
}
IController
namespace System.Web.Mvc
{
    public abstract class ControllerBase : IController
    {    
        //省略其余成员
        
        protected virtual void Execute(RequestContext requestContext)
        {
            if (requestContext == null)
            {
                throw new ArgumentNullException("requestContext");
            }
            if (requestContext.HttpContext == null)
            {
                throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
            }

            VerifyExecuteCalledOnce();
            Initialize(requestContext);

            using (ScopeStorage.CreateTransientScope())
            {
                //执行ExecuteCore方法
                ExecuteCore();
            }
        }
        //该方法在派生类Controller类中实现
        protected abstract void ExecuteCore();
    }
}
ControllerBase
namespace System.Web.Mvc
{
    public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
    {
        //省略其余成员
        
        //实现ControllerBase中的抽象方法
        protected override void ExecuteCore()
        {
            // If code in this method needs to be updated, please also check the BeginExecuteCore() and
            // EndExecuteCore() methods of AsyncController to see if that code also must be updated.
            PossiblyLoadTempData();
            try
            {
                //从路由中获取Action的名字
                string actionName = RouteData.GetRequiredString("action");
                //根据Action名字和上下文对Action进行调用
                if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
                {
                    HandleUnknownAction(actionName);
                }
            }
            finally
            {
                PossiblySaveTempData();
            }
        }
    }
}
Controller

  由上述代码能够看出Action的执行最终实如今Controller类的ExecuteCore方法中,而其中ActionInvoker就是实现Action调用的组件,执行ActionInvoker的InvokeAction方法实现对Action的调用。session

整个执行过程的功能为:【检查对请求只作一次处理】-->【封装请求上下文】-->【获取上一次没有被使用的TempData】-->【过滤器、Action的执行】-->【View的呈现(下一节介绍)】-->【将没有被使用的TempData放入Session中】
app

//整个流程
    public abstract class ControllerBase : IController
    {
        protected virtual void Execute(RequestContext requestContext)
        {
            //检查对请求只作一次处理
            VerifyExecuteCalledOnce();
            //封装请求上下文(RequestContext对象是在路由系统中建立的。其中封装了请求上下文和路由信息。)
            Initialize(requestContext);
            //定义用于包含临时做用域存储的类。    基于 CurrentScope 属性中的做用域,返回用于存储临时做用域内的数据的字典。
            //这个的做用暂时尚未弄清楚,不过经过重写Execute方法,在using块内能够获取ScopeStorage的属性CurrentScope的三个键值对。
            using (ScopeStorage.CreateTransientScope())
            {
                //执行Controller类中的ExecuteCore方法
                ExecuteCore();
            }
        }
    }
    public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
    {    
        protected override void ExecuteCore()
        {
            //获取上一次没有被使用的TempData
            PossiblyLoadTempData();
            try
            {
                //从路由数据中获取请求的Action的名字(路由系统从请求地址中获取)
                string actionName = RouteData.GetRequiredString("action");
                //过滤器、Action的执行、View的呈现
                if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
                {
                    HandleUnknownAction(actionName);
                }
            }
            finally
            {
                //将没有被使用的TempData放入Session中
                PossiblySaveTempData();
            }
        }
    }

 ==从以上的执行过程当中各代码的功能能够看出ExecuteCore()方法是Action执行的主操做,而VerifyExecuteCalledOnce()、Initialize(requestContext)两个方法是前戏了。咱们就先来分析下这两个前戏的方法,主操做ExecuteCore()方法留着最后,并对其内部操做再进行详细分析。框架

 一、VerifyExecuteCalledOnce()less

  此方法保证了一次Http请求只进行一次处理ide

    public abstract class ControllerBase : IController
    {
        private readonly SingleEntryGate _executeWasCalledGate = new SingleEntryGate();

        //RequestContext对象是在路由系统中建立的。其中封装了请求上下文和路由信息。
        protected virtual void Execute(RequestContext requestContext)
        {
            //判断对当期的请求是不是第一次执行处理
            VerifyExecuteCalledOnce();
            //执行Controller类的Initialize方法。(Initialize方法在Controller类中被重写)
            Initialize(requestContext);
            //定义用于包含临时做用域存储的类。    基于 CurrentScope 属性中的做用域,返回用于存储临时做用域内的数据的字典。
            //这个的做用暂时尚未弄清楚,不过经过重写Execute方法,在using块内能够获取ScopeStorage的属性CurrentScope的三个键值对。
            using (ScopeStorage.CreateTransientScope())
            {
                //执行Controller类中的ExecuteCore方法
                ExecuteCore();
            }
        }
        
        internal void VerifyExecuteCalledOnce()
        {
            //若是是TryEnter方法是第一次执行_executeWasCalledGate.TryEnter()=true
            //不然_executeWasCalledGate.TryEnter()=false
            if (!_executeWasCalledGate.TryEnter())
            {
                string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBase_CannotHandleMultipleRequests, GetType());
                throw new InvalidOperationException(message);
            }
        }
        
    }
ControllerBase
    internal sealed class SingleEntryGate
    {
        private const int NotEntered = 0;
        private const int Entered = 1;

        private int _status;

        // returns true if this is the first call to TryEnter(), false otherwise
        public bool TryEnter()
        {
            //Interlocked静态类定义在System.Threading命名空间中且Exchange方法是extern的(定义在别的文件中)!
            //方法的概述为:以原子操做的形式,将 32 位有符号整数设置为指定的值并返回原始值。原子操做是指不会被线程调度机制打断的操做;这种操做一旦开始,就一直运行到结束
            //Interlocked.Exchange方法将Extered的值设置给_status,并返回值为第一个参数_status的原始值
            int oldStatus = Interlocked.Exchange(ref _status, Entered);
            return (oldStatus == NotEntered);
        }
    }
SingleEntryGate

    ==上述代码,在ControllerBase类中私有只读字段_executeWasCalledGate建立了一个SingleEntryGate对象,而VerifyExecuteCalledOnce方法的功能就是经过 这个对象的TryTryEnter方法来实现的!若是TryTryEnter方法返回值为:true,则表示是第一次执行;不然非第一次执行,那么就抛出非法操做异常了。从而保证了一次Http请求只进行一次处理。
而内部究竟是如何实现的呢?咱们就在来看看SingleEntryGate类,其中的TeyEnter方法中调用了Interlocked.Exchange(ref int1,int2)方法,此方法定义的System.Threading命名空间内,方法的功能为:将第二个参数的值赋值给第一个参数,并将第一个参数原来的值做为方法的返回值。
例如:
  若是是第一次调用TryEnter方法【Entered=1,_status=0,NotEntered = 0】执行完Interlocked.Exchange方法后【Entered = 1,_status=1,NotEntered = 0,oldStatus=0】,此时oldStatus=NotEntered = 0,返回true
  若是是第n次调用TryEnter方法,那么此时的变量值仍是第一次执行完的状态【Entered = 1,_status=1,NotEntered = 0】,而执行完Interlocked.Exchange方法后【Entered = 1,_status=1,NotEntered = 0,oldStatus=1】,此时NotEntered = 0,oldStatus=1,不相等,返回false函数

注意,在ControllerBase类中私有只读字段_executeWasCalledGate是非静态的字段,因此实现的功能是【检查每一次请求只执行一次】,若是是静态字段,那么就变成了程序只执行一次请求的处理。(这功能的实现值得收藏)

二、Initialize(requestContext)

  将 requestContext 和 当前请求的控制对象 封装到一个 ControllerContext对象中!其中requestContext是已封装了请求上下文和当前请求的路由信息的一个上下文。

    public abstract class ControllerBase : IController
    {
        public ControllerContext ControllerContext { get; set; }
        
        //RequestContext对象是在路由系统中建立的。其中封装了请求上下文和路由信息。
        protected virtual void Execute(RequestContext requestContext)
        {
            if (requestContext == null)
            {
                throw new ArgumentNullException("requestContext");
            }
            if (requestContext.HttpContext == null)
            {
                throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
            }
            //判断是否为第一次执行
            VerifyExecuteCalledOnce();
            //执行Controller类的Initialize方法。(Initialize方法在Controller类中被重写)
            Initialize(requestContext);
            //定义用于包含临时做用域存储的类。    基于 CurrentScope 属性中的做用域,返回用于存储临时做用域内的数据的字典。
            //这个的做用暂时尚未弄清楚,不过经过重写Execute方法,在using块内能够获取ScopeStorage的属性CurrentScope的三个键值对。
            using (ScopeStorage.CreateTransientScope())
            {
                //执行Controller类中的ExecuteCore方法
                ExecuteCore();
            }
        }
        protected virtual void Initialize(RequestContext requestContext)
        {
            ControllerContext = new ControllerContext(requestContext, this);
        }        
    }
ControllerBase
    public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
    {
        public UrlHelper Url { get; set; }
        
        protected override void Initialize(RequestContext requestContext)
        {
            base.Initialize(requestContext);
            Url = new UrlHelper(requestContext);
        }
    }
Controller
    public class ControllerContext
    {
        internal const string ParentActionViewContextToken = "ParentActionViewContext";
        private HttpContextBase _httpContext;
        private RequestContext _requestContext;
        private RouteData _routeData;

        // parameterless constructor used for mocking
        public ControllerContext()
        {
        }
        protected ControllerContext(ControllerContext controllerContext)
        {
            if (controllerContext == null)
            {
                throw new ArgumentNullException("controllerContext");
            }

            Controller = controllerContext.Controller;
            RequestContext = controllerContext.RequestContext;
        }

        public ControllerContext(HttpContextBase httpContext, RouteData routeData, ControllerBase controller)
            : this(new RequestContext(httpContext, routeData), controller)
        {
        }

        //【Action的执行】过程当中,经过请求上下文和请求的当期控制器对象(如:HomeController)建立控制器上下文。
        public ControllerContext(RequestContext requestContext, ControllerBase controller)
        {
            if (requestContext == null)
            {
                throw new ArgumentNullException("requestContext");
            }
            if (controller == null)
            {
                throw new ArgumentNullException("controller");
            }
            RequestContext = requestContext;//上下文参数设置给私有变量
            Controller = controller;        //控制器(如:HomeController)对象设置给属性
        }

        public virtual ControllerBase Controller { get; set; }

        public IDisplayMode DisplayMode
        {
            get { return DisplayModeProvider.GetDisplayMode(HttpContext); }
            set { DisplayModeProvider.SetDisplayMode(HttpContext, value); }
        }

        public virtual HttpContextBase HttpContext
        {
            get
            {
                if (_httpContext == null)
                {
                    _httpContext = (_requestContext != null) ? _requestContext.HttpContext : new EmptyHttpContext();
                }
                return _httpContext;
            }
            set { _httpContext = value; }
        }

        public virtual bool IsChildAction
        {
            get
            {
                RouteData routeData = RouteData;
                if (routeData == null)
                {
                    return false;
                }
                return routeData.DataTokens.ContainsKey(ParentActionViewContextToken);
            }
        }

        public ViewContext ParentActionViewContext
        {
            get { return RouteData.DataTokens[ParentActionViewContextToken] as ViewContext; }
        }

        public RequestContext RequestContext
        {
            get
            {
                if (_requestContext == null)
                {
                    // still need explicit calls to constructors since the property getters are virtual and might return null
                    HttpContextBase httpContext = HttpContext ?? new EmptyHttpContext();
                    RouteData routeData = RouteData ?? new RouteData();

                    _requestContext = new RequestContext(httpContext, routeData);
                }
                return _requestContext;
            }
            set { _requestContext = value; }
        }

        public virtual RouteData RouteData
        {
            get
            {
                if (_routeData == null)
                {
                    _routeData = (_requestContext != null) ? _requestContext.RouteData : new RouteData();
                }
                return _routeData;
            }
            set { _routeData = value; }
        }

        private sealed class EmptyHttpContext : HttpContextBase
        {
        }
    }
ControllerContext

    ==上述代码,对于Initialize方法的执行,因为ControllerBase中的Initialize方法在派生类Controller类中被重写,因此要执行Controller类中的Initialize方法。方法内首先调用了父类ControllerBase中的Initialize方法建立了一个控制器上下文对象,并赋值给一个公有属性。以后又建立了UrlHelper对象也赋值给了一个公有属性。这个控制器上下文对象包含了从请求到如今的全部有用的数据,因此在以后对请求处理的步骤中随处可见!这个UrlHelper对象还没用到,暂且不议。

三、ExecuteCore()

  ControllerBase类中定义了抽象方法ExecuteCore,该方法被派生类Controller类中实现!因此,要执行Controller类中的ExecuteCore方法,如上所言,此方法是Action执行的主操做,其中先对上一次操做没有被TempData作处理,而后执行过滤器和请求的Action,最后进行View的呈现(下一节再对View的呈现作分析,如今只需知道这个执行的流程便可)。下面,咱们就来用分析全部过程的代码!

    public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
    {
        protected override void ExecuteCore()
        {
            //获取上次处理过程当中没有被使用的TempData
            PossiblyLoadTempData();
            try
            {
                //从路由数据中获取请求的Action的名字
                string actionName = RouteData.GetRequiredString("action");
                //ActionInvoker为一个AsyncControllerActionInvoker对象
                if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
                {
                    HandleUnknownAction(actionName);
                }
            }
            finally
            {
                //将TempData保存到Session中。等待以后将Session的key【__ControllerTempData】发送到响应流中!
 PossiblySaveTempData();
            }
        }
    }

3-一、PossiblyLoadTempData()

  此方法建立保存TempData的集合并获取上一次请求中没有被使用TempData添加到以前建立的那个集合中!

在介绍这个方法以前,有必要先了解下MVC框架下TempData的机制:
客户端向服务发请求时,程序在执行本次请求的Action前,会先建立一个用来保存TempData的集合A,而后根据key=__ControllerTempData去服务器Session中获取值并转换为Dictionary<string, object>类型,若是获得的值为null,表示三种状况(一、当前是客户端第一次向服务端发送请求。二、上次请求中没有定义TempData值。三、上次请求中的TempData被View中用完了。);若是获得的值不为null,则表示上一次对请求的处理时TempData没有被使用完,此时获取的值就是上次处理请求时没有被使用的TempData集合,而后将这集合赋值给咱们开始建立的用于保存TempData的集合A中,再将key=__ControllerTempData的Session移除;以后执行Action方法内的代码时,将本次的请求的Action中定义的TempData[""]=XXX也添加到最开始建立的那个集合A中,执行完Action方法后,在View的呈现中,若是使用了TempData,就将这个TempData从集合A中移除,执行完View后,则将保存TempData的集合A看成value,key=__ControllerTempData保存到服务器Session中。最后将全部Session的keys发送到响应流中!

上述提到那个保存TempData的集合A其实是TempDataDictionary类型中的一个变量,该变量是一个字典类对象(new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase))
TempData是ControllerBase类中的一个类型为TempDataDictionary的属性,TempData属性其实就是一个TempDataDictionary对象,该对象中有一个字典类型的私有字段保存着全部TempData的值!
咱们在Action方法中使用的TempData["kk"]=XX,其实就是调用的TempDataDictionary对象的索引器,将该键值对添加到保存全部TempData的私有字典表中!
扩展:Session的机制,Session保存在服务器,可是请求的最后服务端会将Session的全部key发送到响应流中!当再次发来请求时,能够从请求上下文中获取到全部的keys。

    public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
    {
        protected override void ExecuteCore()
        {
            //获取上次处理过程当中没有被使用的TempData
            PossiblyLoadTempData();
            try
            {
                //从路由数据中获取请求的Action的名字
                string actionName = RouteData.GetRequiredString("action");
                //ActionInvoker为一个AsyncControllerActionInvoker对象
                if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
                {
                    HandleUnknownAction(actionName);
                }
            }
            finally
            {
                //将TempData保存到Session中。等待以后将Session的key【__ControllerTempData】发送到响应流中!
                PossiblySaveTempData();
            }
        }
        
        private ITempDataProvider _tempDataProvider;
        
        internal IDependencyResolver Resolver
        {
            get { return _resolver ?? DependencyResolver.CurrentCache; }
            set { _resolver = value; }
        }
        
        public ITempDataProvider TempDataProvider
        {
            get
            {
                if (_tempDataProvider == null)
                {
                    _tempDataProvider = CreateTempDataProvider();
                }
                return _tempDataProvider;
            }
            set { _tempDataProvider = value; }
        }
        
        protected virtual ITempDataProvider CreateTempDataProvider()
        {
            //Resolver是Controller的一个属性,返回值为:DependencyResolver.CurrentCache
            //Resolver.GetService<ITempDataProvider>() 能够不用看,由于返回的是null。这个方法其实先根据key=ITempDataProvider从全局的缓存表中查找值,若是有值,则返回。若是没有,则利用DefaultDependencyResolver对象经过反射
            //根据类型ITempDataProvider建立对象。可是因为这里的ITempDataProvider是接口,而DefaultDependencyResolver对象反射建立对象时,若是是接口或抽象类均返回null。详细请看DependencyResolver类!
            //因此这段代码,返回值就是 new SessionStateTempDataProvider();
            return Resolver.GetService<ITempDataProvider>() ?? new SessionStateTempDataProvider();
        }
        
        internal void PossiblyLoadTempData()
        {
            //检查当前请求的Action是否为子Action。
            //即:在View中是否应用了@Html.Action(),若是在View中嵌套了一个@Html.Action(action2),则这个action2也是须要再被执行一次的!即便Action的View中嵌套了一个Action,整个过程仍是被
            //      认为是一次请求的,此处判断条件的目的是防止被嵌套的Action再去执行。
            if (!ControllerContext.IsChildAction)
            {
                //TempData是基类ControllerBase中的属性
                //此处是TempData属性被第一次使用,获得TempDataDictionary对象后,再执行此对象的Load方法,这个方法就是去实现获取上一次处理请求时没使用的TempData功能的!
                //参数TempDataProvider是关键,它是Controller类的一个属性(就是一个SessionStateTempDataProvider对象),正如参数名的翻译这个参数就是负责去请求上下文中检查并获取值的!
                TempData.Load(ControllerContext, TempDataProvider);
            }
        }
        
    }    
Controller
    public abstract class ControllerBase : IController
    {

        private TempDataDictionary _tempDataDictionary;
        //此时的ControllerContext是已经封装 请求上下文、路由、当情请求的控制器对象。(在执行ExecuteCore方法以前的Execute方法中设置的!)
        public ControllerContext ControllerContext { get; set; }

        //第一次使用TempData属性时(即:PossiblyLoadTempData方法是第一次使用),会建立一个TempDataDictionary对象并赋值给变量_tempDataDictionary,属性的返回值就是该_tempDataDictionary变量。
        //之后再使用TempData属性时,返回都是第一次建立的那个TempDataDictionary对象,因此咱们在Action方法中使用TempData[""]时,就是调用TempDataDictionary对象的索引器,向
        //保存TempData集合添加一个键值对。
        public TempDataDictionary TempData
        {
            get
            {
                if (ControllerContext != null && ControllerContext.IsChildAction)
                {
                    return ControllerContext.ParentActionViewContext.TempData;
                }
                if (_tempDataDictionary == null)
                {
                    //实例化TempDataDictionary,在构造函数中会建立一个不区分key大小写的字典表,并赋值给一个私有字段。
                    _tempDataDictionary = new TempDataDictionary();
                }
                return _tempDataDictionary;
            }
            set { _tempDataDictionary = value; }
        }
    }
ControllerBase
    public class TempDataDictionary : IDictionary<string, object>
    {
        internal const string TempDataSerializationKey = "__tempData";

        private Dictionary<string, object> _data;
        private HashSet<string> _initialKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        private HashSet<string> _retainedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        public TempDataDictionary()
        {
            //初始化key不区分大小写的字典类
            _data = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        }

        public ICollection<string> Keys
        {
            get { return _data.Keys; }
        }
        //TempData["MyKey"]=MyValue,就是调用此索引器
        //使用TempData时候,也是用这个索引,以后介绍!
        public object this[string key]
        {
            get
            {
                object value;
                if (TryGetValue(key, out value))
                {
                    _initialKeys.Remove(key);
                    return value;
                }
                return null;
            }
            set
            {
                _data[key] = value;
                _initialKeys.Add(key);
            }
        }

        public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
        {
            //从Session中获取上一次请求中没有被使用的TempData
            IDictionary<string, object> providerDictionary = tempDataProvider.LoadTempData(controllerContext);
            //若是从Session中获取的值不为null,则将Session获取的值赋值给_data(将上次没使用的TempData再次添加到集合中)。
            //这里的判断providerDictionary != null其实是判断取值是否出错。
            //由于tempDataProvider.LoadTempData(controllerContext)方法从Session中获取值时,若是有值,则返回一个其中有键值对IDictionary<string, object>对象,不然返回一个没有值的IDictionary<string, object>对象
            _data = (providerDictionary != null)
                ? new Dictionary<string, object>(providerDictionary, StringComparer.OrdinalIgnoreCase)
                : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            //并将TempData的全部key保存到_initialKeys中!
            _initialKeys = new HashSet<string>(_data.Keys, StringComparer.OrdinalIgnoreCase);
            //清空_retainedKeys中的值,因此默认状况下,_retainedKeys的值为空
            _retainedKeys.Clear();
        }
    }
TempDataDictionary
    public class SessionStateTempDataProvider : ITempDataProvider
    {
        internal const string TempDataSessionStateKey = "__ControllerTempData";

        public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
        {
            HttpSessionStateBase session = controllerContext.HttpContext.Session;
            //这里不是判断上下文中是否含有Session的key,应该是请求是否出现异常!
            if (session != null)
            {    
                //根据key=__ControllerTempData去服务器的Session中获取值
                Dictionary<string, object> tempDataDictionary = session[TempDataSessionStateKey] as Dictionary<string, object>;
                //若是Session中保存key为__ControllerTempData的值不为空,表示:上次处理请求时存在么有被使用的TempData,那么就返回这个字典对象(以后再添加到保存TempData的集合中,以供本次请求使用)。
                //并将服务器的key=__ControllerTempData的Session移除!
                if (tempDataDictionary != null)
                {
                    session.Remove(TempDataSessionStateKey);
                    return tempDataDictionary;
                }
            }
            return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        }   
    }
SessionStateTempDataProvider
    public class DependencyResolver
    {
        //静态字段,因此在程序执行前就已经执行了该类的构造函数。
        private static DependencyResolver _instance = new DependencyResolver();
        private IDependencyResolver _current;
        private CacheDependencyResolver _currentCache;
        
        internal static IDependencyResolver CurrentCache
        {
            get { return _instance.InnerCurrentCache; }
        }
        internal IDependencyResolver InnerCurrentCache
        {
            get { return _currentCache; }
        }

        //构造函数
        public DependencyResolver()
        {
            InnerSetResolver(new DefaultDependencyResolver());
        }

        //构造函数调用此方法
        public void InnerSetResolver(IDependencyResolver resolver)
        {
            //参数resolver=new DefaultDependencyResolver()
            //DefaultDependencyResolver类的功能为:经过反射根据类型建立对象!
            _current = resolver;
            _currentCache = new CacheDependencyResolver(_current);
        }

        //由于DependencyResolver只在静态字段中进行了实例化,全部这个CacheDependencyResolver也只实例化一次。因此,ConcurrentDictionary字典也只实例化一次,故,在一次请求中这个集合是一直存在的!
        private sealed class CacheDependencyResolver : IDependencyResolver
        {
            private readonly ConcurrentDictionary<Type, object> _cache = new ConcurrentDictionary<Type, object>();
            private readonly ConcurrentDictionary<Type, IEnumerable<object>> _cacheMultiple = new ConcurrentDictionary<Type, IEnumerable<object>>();
            private readonly Func<Type, object> _getServiceDelegate;
            private readonly Func<Type, IEnumerable<object>> _getServicesDelegate;

            private readonly IDependencyResolver _resolver;

            public CacheDependencyResolver(IDependencyResolver resolver)
            {
                _resolver = resolver;
                //这个委托就是DefaultDependencyResolver实例的GetService方法,利用类型建立实例。
                //目的就是当集合中没有类型对应的值(实例)时,就经过DefaultDependencyResolver的GetService方法去再去反射建立实例。
                _getServiceDelegate = _resolver.GetService;
                _getServicesDelegate = _resolver.GetServices;
            }

            public object GetService(Type serviceType)
            {
                //先根据serviceType去集合的key中找,若是对应的key有值,则直接将值返回。若是没有,则将serviceType做为参数执行_getServiceDelegate委托,而后将执行委托后的返回值(利用类型建立对象)返回,并将此serviceType和执行委托后的返回值添加到集合中!
                return _cache.GetOrAdd(serviceType, _getServiceDelegate);
            }

            public IEnumerable<object> GetServices(Type serviceType)
            {
                // Use a saved delegate to prevent per-call delegate allocation
                return _cacheMultiple.GetOrAdd(serviceType, _getServicesDelegate);
            }
        }

        private class DefaultDependencyResolver : IDependencyResolver
        {
            public object GetService(Type serviceType)
            {
                if (serviceType.IsInterface || serviceType.IsAbstract)
                {
                    return null;
                }

                try
                {
                    return Activator.CreateInstance(serviceType);
                }
                catch
                {
                    return null;
                }
            }

            public IEnumerable<object> GetServices(Type serviceType)
            {
                return Enumerable.Empty<object>();
            }
        

            private Func<Type, object> _getService;
            private Func<Type, IEnumerable<object>> _getServices;

            public DelegateBasedDependencyResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices)
            {
                _getService = getService;
                _getServices = getServices;
            }

            [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This method might throw exceptions whose type we cannot strongly link against; namely, ActivationException from common service locator")]
            public object GetService(Type type)
            {
                try
                {
                    return _getService.Invoke(type);
                }
                catch
                {
                    return null;
                }
            }

            public IEnumerable<object> GetServices(Type type)
            {
                return _getServices(type);
            }
        }
    }
DependencyResolver

   上述代码,TempDataDictionary类就是程序中使用的TempData的类型,SessionStateTempDataProvider类用于服务器Session中获取上一次没有被使用的TempData的集合,DependencyResolver类在上一节的Controller激活中已经介绍过了,它主要是用于根据类型经过反射建立实例(还具备缓存的功能)。
补充:对于咱们定义的TempData["kk"]=value,若是在使用时也是执行的TempDataDictionary类的索引器,在索引器的Set中可看到,若是使用TempData以后,是在【_initialKeys】中将key移除,而不是直接在保存全部TempData的集合【_data】中移除。
猜测:MVC3和MVC4中对TempData的使用不同

 3-二、ActionInvoker.InvokeAction(ControllerContext, actionName)

   此代码实现了:Action的执行、过滤器的执行、View的呈现。
  因为这部分的内容太多,为避免影响知识点混乱,将再开一篇博文来详细介绍!《白话学习MVC(八)Action的执行2》

3-三、PossiblySaveTempData()

  从保存着全部TempData的集合中移除已经被使用的TempData,最后再将全部没有被使用TempData集合保存在key=__ControllerTempData的Session中!以便下次请求中使用。

    public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
    {
        protected override void ExecuteCore()
        {
            //获取上次处理过程当中没有被使用的TempData
            PossiblyLoadTempData();
            try
            {
                //从路由数据中获取请求的Action的名字
                string actionName = RouteData.GetRequiredString("action");
                //ActionInvoker为一个AsyncControllerActionInvoker对象
                if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
                {
                    HandleUnknownAction(actionName);
                }
            }
            finally
            {
                //将TempData保存到Session中。等待以后将Session的key【__ControllerTempData】发送到响应流中!
                PossiblySaveTempData();
            }
        }
        private ITempDataProvider _tempDataProvider;
        
        internal IDependencyResolver Resolver
        {
            get { return _resolver ?? DependencyResolver.CurrentCache; }
            set { _resolver = value; }
        }
        
        public ITempDataProvider TempDataProvider
        {
            get
            {
                if (_tempDataProvider == null)
                {
                    _tempDataProvider = CreateTempDataProvider();
                }
                return _tempDataProvider;
            }
            set { _tempDataProvider = value; }
        }
        
        protected virtual ITempDataProvider CreateTempDataProvider()
        {
            //Resolver是Controller的一个属性,返回值为:DependencyResolver.CurrentCache
            //Resolver.GetService<ITempDataProvider>() 能够不用看,由于返回的是null。这个方法其实先根据key=ITempDataProvider从全局的缓存表中查找值,若是有值,则返回。若是没有,则利用DefaultDependencyResolver对象经过反射
            //根据类型ITempDataProvider建立对象。可是因为这里的ITempDataProvider是接口,而DefaultDependencyResolver对象反射建立对象时,若是是接口或抽象类均返回null。详细请看DependencyResolver类!
            //因此这段代码,返回值就是 new SessionStateTempDataProvider();
            return Resolver.GetService<ITempDataProvider>() ?? new SessionStateTempDataProvider();
        }
        
        internal void PossiblySaveTempData()
        {
            if (!ControllerContext.IsChildAction)
            {
                //最后一次使用TempData,TempData属性定义在基类ControllerBase中
                TempData.Save(ControllerContext, TempDataProvider);
            }
        }
    
    }    
    
Controller
    public abstract class ControllerBase : IController
    {

        private TempDataDictionary _tempDataDictionary;
        //此时的ControllerContext是已经封装 请求上下文、路由、当情请求的控制器对象。(在执行ExecuteCore方法以前的Execute方法中设置的!)
        public ControllerContext ControllerContext { get; set; }

        //第一次使用TempData属性时(即:PossiblyLoadTempData方法是第一次使用),会建立一个TempDataDictionary对象并赋值给变量_tempDataDictionary,属性的返回值就是该_tempDataDictionary变量。
        //之后再使用TempData属性时,返回都是第一次建立的那个TempDataDictionary对象,因此咱们在Action方法中使用TempData[""]时,就是调用TempDataDictionary对象的索引器,向
        //保存TempData集合添加一个键值对。
        //此时不是第一次使用TempData,应该是最后一次了
        public TempDataDictionary TempData
        {
            get
            {
                if (ControllerContext != null && ControllerContext.IsChildAction)
                {
                    return ControllerContext.ParentActionViewContext.TempData;
                }
                if (_tempDataDictionary == null)
                {
                    //实例化TempDataDictionary,在构造函数中会建立一个不区分key大小写的字典表,并赋值给一个私有字段。
                    _tempDataDictionary = new TempDataDictionary();
                }
                return _tempDataDictionary;
            }
            set { _tempDataDictionary = value; }
        }
    }
ControllerBase
    public class TempDataDictionary : IDictionary<string, object>
    {
        private Dictionary<string, object> _data;
        private HashSet<string> _initialKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        private HashSet<string> _retainedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        
        //没有执行Action以前,对TempData的操做
        public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
        {
            //从Session中获取上一次请求中没有被使用的TempData
            IDictionary<string, object> providerDictionary = tempDataProvider.LoadTempData(controllerContext);
            //若是从Session中获取的值不为null,则将Session获取的值赋值给_data(将上次没使用的TempData再次添加到集合中)。
            //这里的判断providerDictionary != null其实是判断取值是否出错。
            //由于tempDataProvider.LoadTempData(controllerContext)方法从Session中获取值时,若是有值,则返回一个其中有键值对IDictionary<string, object>对象,不然返回一个没有值的IDictionary<string, object>对象
            _data = (providerDictionary != null)
                ? new Dictionary<string, object>(providerDictionary, StringComparer.OrdinalIgnoreCase)
                : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            //并将TempData的全部key保存到_initialKeys中!
            _initialKeys = new HashSet<string>(_data.Keys, StringComparer.OrdinalIgnoreCase);
            //清空_retainedKeys中的值
            _retainedKeys.Clear();
        }
        
        public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
        {
            //RemoveFromDictionary方法是MVC中为IDictionary<TKey, TValue>的建立的一个扩展方法。
            //RemoveFromDictionary方法做用:检查TempData是否被使用过,若是已被使用,则将其在保存全部TempData集合(_data)中移除。
            //须知:当使用TempData时,只是在_initialKeys中将key移除,并无在保存全部TempData集合(_data)中移除(见TempDataDictionary类的索引器)
            _data.RemoveFromDictionary((KeyValuePair<string, object> entry, TempDataDictionary tempData) =>
                {
                    string key = entry.Key;
                    //只有当前TempDataDictionary对象的_initialKeys和_retainedKeys中都不含有_data中的key时,委托才返回true。
                    //_initialKeys和_retainedKeys初始化都是在 Load 方法中,_initialKeys中的值为全部TempData的key,而_retainedKeys是为空的集合
                    //目前所知,由于retainedKeys是为空的集合,因此tempData._retainedKeys.Contains(key)为:false
                    //而当使用TempData时,只是在_initialKeys中将key移除。因此,若是是被使用过的TempData,则tempData._initialKeys.Contains(key)为:false,不然都为true
                    //最终得出:若是当前循环的TempData被使用了,则返回ture,不然返回false
                    return !tempData._initialKeys.Contains(key)&& !tempData._retainedKeys.Contains(key);
                }, this);
            //将全部没有被使用的TempData的集合添加到服务器Session中,以便下次请求再使用
            tempDataProvider.SaveTempData(controllerContext, _data);
        }
        
    }
    
TempDataDictionary
    internal static class DictionaryExtensions
    {
        //此时的TState类型为:TempDataDictionary
        public static void RemoveFromDictionary<TKey, TValue, TState>(this IDictionary<TKey, TValue> dictionary, Func<KeyValuePair<TKey, TValue>, TState, bool> removeCondition, TState state)
        {
            Contract.Assert(dictionary != null);
            Contract.Assert(removeCondition != null);

            int removeCount = 0;
            TKey[] keys = new TKey[dictionary.Count];
            //循环全部的TempData,让每一个循环项执行委托removeCondition
            foreach (var entry in dictionary)
            {
                //对于委托执行removeCondition若是当前循环的TempData被使用了,则返回ture,不然返回false
                if (removeCondition(entry, state))
                {
                    //将已经被使用的TempData的key添加到keys数组中
                    //removeCount++表示已经被使用的TempData个数。
                    keys[removeCount] = entry.Key;
                    removeCount++;
                }
            }
            //循环被删除的TempData个数,将该键值对在保存全部TempData的集合中移除。
            for (int i = 0; i < removeCount; i++)
            {
                dictionary.Remove(keys[i]);
            }
        }
    }
    
DictionaryExtensions
    public class SessionStateTempDataProvider : ITempDataProvider
    {
        internal const string TempDataSessionStateKey = "__ControllerTempData";
        
        public virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
        {
            if (controllerContext == null)
            {
                throw new ArgumentNullException("controllerContext");
            }

            HttpSessionStateBase session = controllerContext.HttpContext.Session;
            bool isDirty = (values != null && values.Count > 0);
            //这里的判断:不是没有Sesson,由于即便是第一次请求到来时(无任何Session),这个对象也不为空。
            //而是根据上下文因为建立HttpSessionStateBase对象时出问题,该对象为空。
            if (session == null)
            {
                if (isDirty)
                {
                    throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled);
                }
            }
            //HttpSessonStateBase对象建立没问题
            else
            {    
                //若是保存TempData的字典类实例中有值(也就是定义的TempData没有在view中被全使用),将值设置给key为__ControllerTempData的Session。
                if (isDirty)
                {
                    session[TempDataSessionStateKey] = values;
                }
                //TempData在view中所有已被使用
                else
                {
                    //而且Session中该能够__ControllerTempData还有对应的值
                    //若是用户在Action中本身定义一个key为__ControllerTempData的Session,这里移除Session中的这个值,防止了将这个key发送到相应流,以伪形成未被利用的TempData
                    if (session[TempDataSessionStateKey] != null)
                    {
                        session.Remove(TempDataSessionStateKey);
                    }
                }
            }
        }
    
   }
SessionStateTempDataProvider

   上述代码,PossiblySaveTempData方法执行 TempData.Save(ControllerContext, TempDataProvider)来完成全部的功能,TempData获得的以前建立的TempDataDictionary对象(该对象的_data字段保存这定义的全部TempData键值对),参数TempDataProvider是建立的SessionStateTempDataProvider对象(该对象的SaveTempData方法的做用就是将没有被使用的TempData保存到Session中)。因此,TempDataDictionary对象的Save方法首先去遍历全部的TempData,检查TempData是否被使用过了,若是已被使用,则将该TempData在保存全部TempData的集合中移除,最后执行SessionStateTempDataProvider对象的SaveTempData方法将通过处理后的集合添加到Session中,以便这些TempData在写一次请求中使用!
  如3-1中补充到,对于咱们定义的TempData["kk"]=value,若是在使用时是执行的TempDataDictionary类的索引器,在索引器的Set中可看到,若是使用TempData以后,是在【_initialKeys】中将key移除,而不是直接在保存全部TempData的集合【_data】中移除。此处作的就是在根据【_initialKeys】将被使用的TempData在【_data】中移除。

 

 以上就是所有内容,若有不合适之处请指教!

遗留问题:

  一、如何利用各扩展点暂没有分析。

疑问:

  一、既然利用DependencyResolver经过反射建立对象时,接口和抽象类都不能够,MVC中为何还在使用GetService方法使用接口来建立对象呀?明知是Null

 更正:

  一、以上的Controller类的继承关系是MVC5中的,在MVC4中应该是以下,MVC4中没有引进IAuthenticationFilter过滤器。

public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
    {
        ...
    }
相关文章
相关标签/搜索