前不久在做mvc跨控制器传值的时候发现二个标题,就是偶尔候TempData的值为null,然后查阅了好多资料,发现了好多都以逻辑和公理什么的(想看规律能够查看原理的稿子,本文是用法),可是的确化解的不2法门怎样案例都未有,

近年抽空看了瞬间ASP.NET
MVC的壹些源码,顺带写篇小说做个笔记以便日后查看。

近日忙里偷闲看了1晃ASP.NET
MVC的片段源码,顺带写篇文章做个笔记以便日后查看。

本种类重大翻译自《ASP.NET MVC Interview Questions and Answers 》- By
Shailendra
Chauhan,想看英文原版的可访问自行下载。该书首要分为两局地,ASP.NET
MVC 5、ASP.NET WEB
API贰。本书最大的特色是以面试问答的样式展开进行。通读此书,会扶助您对ASP.NET
MVC有越来越深层次的精通。
由于个人技术水平和英文水准也是零星的,由此错误在所难免,希望大家多多留言指正。
洋洋洒洒导航
Asp.net mvc 知多少(一)
Asp.net mvc 知多少(二)
Asp.net mvc 知多少(三)
Asp.net mvc 知多少(四)
Asp.net mvc 知多少(五)

于是就把自身的代码当成案例给贴出来,方便更加直观的化解难题。

在UrlRoutingModule模块中,将请求处理程序映射到了MvcHandler中,由此,聊到Controller的激活,首先要从MvcHandler入手,MvcHandler达成了五个接口:IHttpAsyncHandler,
IHttpHandler, IRequiresSessionState。
其拍卖逻辑首要实未来同步和异步的ProcessRequest方法中,总的来说,该措施在进行的时候,大约经历以下多少个步骤:

在UrlRoutingModule模块中,将请求处理程序映射到了MvcHandler中,由此,说到Controller的激活,首先要从MvcHandler动手,MvcHandler完成了多个接口:IHttpAsyncHandler,
IHttpHandler, IRequiresSessionState。
其处理逻辑首要达成在一道和异步的ProcessRequest方法中,总的来说,该办法在实施的时候,大致经历以下多少个步骤:

本节首要教授三种页面传值格局和http请求与action的照射

因为TempData生命周期确实非常短,所以需求持久化一下:

  1. 预处理(在响应头中添加版本音讯并剔除未赋值的可选路由参数)
  2. 通过ControllerBuilder获取ControlerFactory,并应用Controller工厂创造Controller
  3. 依据是或不是是异步处理,调用Controller中相应的点子(ExecuteCore或BeginExecute)
  4. 释放Controller
  1. 预处理(在响应头中添加版本新闻并剔除未赋值的可选路由参数)
  2. 通过ControllerBuilder获取ControlerFactory,并行使Controller工厂创立Controller
  3. 根据是还是不是是异步处理,调用Controller中相应的主意(ExecuteCore或BeginExecute)
  4. 释放Controller

Q50. 介绍下ViewData, ViewBag, TempData 和 Session间的不相同之处?
Ans. 在ASP.NET MVC 中有三种艺术从controller传值到view中:ViewData,
ViewBag 和 TempData。Asp.net WebForm
中能够在1遍用户会话中运用Session去持久化数据。

        public ActionResult Index()
        {
            TempData["message"] = "123asd";
            return view();
        }

        public ActionResult GetTemData()
        {
            var foredid = TempData["message"].ToString();
            var  result=_content.userinfo(foredid);
            return View();
        }

中间第1步在ProcessRequestInit方法中展开拍卖,本文首若是分析第两步中的controller是何等创造出来的。

个中第壹步在ProcessRequestInit方法中展开始拍片卖,本文首若是分析第两步中的controller是怎么成立出来的。

金沙注册送58 1

在此时此刻Action方法中调用Keep方法则保障在现阶段乞求中TempData对象中所存储的键都不会被移除。

Controller的创始是通过ControllerFactory完成的,而ControllerFactory的创造又是在ControllerBuilder中形成的,由此我们先理解一下ControllerBuilder的干活原理。

Controller的开创是通过ControllerFactory完毕的,而ControllerFactory的创始又是在ControllerBuilder中做到的,因而我们先通晓一下ControllerBuilder的做事原理。

ViewData

 

ControllerBuilder

从源码中得以看出,在ControllerBuilder类中,并从未直接完毕对controller工厂的创制,ControllerFactory的创制实际上是信托给壹个继续自IResolver接口的SingleServiceResolver类的实例来达成的,那一点从GetControllerFactory方法中能够旁观,它是透过调用SingleServiceResolver对象的Current属性来成功controller工厂的始建的。

public IControllerFactory GetControllerFactory()
{
    return _serviceResolver.Current;  //依赖IResolver接口创建工厂
}

而且在源码中还发现,SingleServiceResolver类是internal级别的,那意味外部不可能直接访问,那么ControllerBuilder是哪些依靠SingleServiceResolver来落成工厂的挂号呢?继续看代码,ControllerBuilder类和SingleServiceResolver类都有三个Func<IControllerFactory>项目标信托字段,大家姑且称为工厂委托,

//ControllerBuilder.cs
private Func<IControllerFactory> _factoryThunk = () => null;  //工厂委托
//SingleServiceResolver.cs
private Func<TService> _currentValueThunk;  //工厂委托

该信托达成了工厂的创导,而由此SetControllerFactory方法只有是改变了ControllerBuilder类的工厂委托字段,并从未更改SingleServiceResolver类的厂子委托字段,

public void SetControllerFactory(IControllerFactory controllerFactory)
{
    if (controllerFactory == null)
    {
        throw new ArgumentNullException("controllerFactory");
    }

    _factoryThunk = () => controllerFactory;  //更改ControllerBuilder的工厂委托字段
}

由此必须将相应的改观应用到SingleServiceResolver类中才能促成真正的登记,大家通晓,如若是仅仅的引用赋值,那么更改1个引用并不会对其它八个引用造成改变,比如:

Func<object> f1 = ()=>null;
Func<object> f2 = f1;  //f1与f2指向同一个对象
object o = new object();
f1 = ()=>o;  //更改f1后,f2仍然指向之前的对象
bool b1 = f1() == o;   //true
bool b2 = f2() == null;  //true,  f1()!=f2()

所以,ControllerBuilder在实例化SingleServiceResolver对象的时候,并未将本人的工厂委托字段直接赋值给SingleServiceResolver对象的附和字段(因为如此的话SetControllerFactory方法注册的委托不可能使用到Single瑟维斯Resolver对象中),而是经过信托来进行了包装,那样就会形成2个闭包,在闭包中实行引用,如下所示:

Func<object> f1 = ()=>null;
Func<object> f2 = ()=>f1();  //通过委托包装f1,形成闭包
object o = new object();
f1 = ()=>o;  //更改f1后,f2与f1保持同步
bool b1 = f1() == o;  //true
bool b2 = f2() == o;  //true,  f1()==f2()

//ControllerBuilder.cs
internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
{
    _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
                                              () => _factoryThunk(),  //封装委托,闭包引用
                                              new DefaultControllerFactory { ControllerBuilder = this },
                                              "ControllerBuilder.GetControllerFactory");
}

如此那般SingleServiceResolver对象中的工厂委托就会与ControllerBuilder对象中的对应字段保持同步了,SetControllerFactory方法也就直达了交替暗中同意工厂的目标。

闭包引用测试代码:

using System;

class Program
{
    public static void Main(string[] args)
    {
        Func<object> f1 = ()=>null;
        Func<object> f2 = f1;  //f1与f2指向同一个对象
        object o = new object();
        f1 = ()=>o;  //更改f1后,f2仍然指向之前的对象
        bool b1 = f1() == o;   //true
        bool b2 = f2() == null;  //true,  f1()!=f2()

        Print("直接赋值:");
        Print(f1(),"f1() == {0}");
        Print(f2(),"f2() == {0}");
        Print(f1() == f2(),"f1() == f2() ? {0}");

        Func<object> ff1 = ()=>null;
        Func<object> ff2 = ()=>ff1();  //通过委托包装f1,形成闭包
        object oo = new object();
        ff1 = ()=>oo;  //更改f1后,f2与f1保持同步
        bool bb1 = ff1() == oo;  //true
        bool bb2 = ff2() == oo;  //true,  f1()==f2()

        Print("委托赋值:");
        Print(ff1(),"ff1() == {0}");
        Print(ff2(),"ff2() == {0}");
        Print(ff1() == ff2(),"ff1() == ff2() ? {0}");

        Console.ReadLine();
    }

    static void Print(object mess,string format = "{0}")
    {
        string message = mess == null ? "null" : mess.ToString();
        Console.WriteLine(string.Format(format,message));
    }
}

下边看一下Single瑟维斯Resolver类是什么落到实处目的的创立的,该类是个泛型类,那意味着能够协会任何类型的指标,不仅限于ControllerFactory,实际上在MVC中,该类在不少地点都取得了采取,例如:ControllerBuilder、DefaultControllerFactory、BuildManagerViewEngine等,达成了对二种指标的开创。

ControllerBuilder

从源码中能够看来,在ControllerBuilder类中,并未一向促成对controller工厂的开创,ControllerFactory的开创实际上是寄托给二个后续自IResolver接口的SingleServiceResolver类的实例来促成的,那一点从GetControllerFactory方法中得以看出,它是经过调用SingleServiceResolver对象的Current属性来形成controller工厂的创制的。

public IControllerFactory GetControllerFactory()
{
    return _serviceResolver.Current;  //依赖IResolver接口创建工厂
}

并且在源码中还发现,SingleServiceResolver类是internal级其余,那代表外部无法直接访问,那么ControllerBuilder是什么借助SingleServiceResolver来达成工厂的挂号呢?继续看代码,ControllerBuilder类和SingleServiceResolver类都有贰个Func<IControllerFactory>关于MVC中TempData持久化难题。品种的委托字段,我们方今称为工厂委托,

//ControllerBuilder.cs
private Func<IControllerFactory> _factoryThunk = () => null;  //工厂委托
//SingleServiceResolver.cs
private Func<TService> _currentValueThunk;  //工厂委托

该信托达成了工厂的成立,而经过SetControllerFactory方法唯有是改变了ControllerBuilder类的工厂委托字段,并从未改动SingleServiceResolver类的厂子委托字段,

public void SetControllerFactory(IControllerFactory controllerFactory)
{
    if (controllerFactory == null)
    {
        throw new ArgumentNullException("controllerFactory");
    }

    _factoryThunk = () => controllerFactory;  //更改ControllerBuilder的工厂委托字段
}

由此必须将相应的改动应用到SingleServiceResolver类中才能兑现真正的挂号,大家清楚,借使是单纯的引用赋值,那么更改四个引用并不会对其余三个引用造成改变,比如:

Func<object> f1 = ()=>null;
Func<object> f2 = f1;  //f1与f2指向同一个对象
object o = new object();
f1 = ()=>o;  //更改f1后,f2仍然指向之前的对象
bool b1 = f1() == o;   //true
bool b2 = f2() == null;  //true,  f1()!=f2()

由此,ControllerBuilder在实例化SingleServiceResolver对象的时候,并不曾将小编的工厂委托字段直接赋值给SingleServiceResolver对象的附和字段(因为如此的话SetControllerFactory方法注册的信托无法运用到SingleServiceResolver对象中),而是经过信托来展开了包装,那样就会形成四个闭包,在闭包中实行引用,如下所示:

Func<object> f1 = ()=>null;
Func<object> f2 = ()=>f1();  //通过委托包装f1,形成闭包
object o = new object();
f1 = ()=>o;  //更改f1后,f2与f1保持同步
bool b1 = f1() == o;  //true
bool b2 = f2() == o;  //true,  f1()==f2()

//ControllerBuilder.cs
internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
{
    _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
                                              () => _factoryThunk(),  //封装委托,闭包引用
                                              new DefaultControllerFactory { ControllerBuilder = this },
                                              "ControllerBuilder.GetControllerFactory");
}

如此那般Single瑟维斯Resolver对象中的工厂委托就会与ControllerBuilder对象中的对应字段保持同步了,SetControllerFactory方法也就实现了交替暗中认可工厂的目标。

闭包引用测试代码:

using System;

class Program
{
    public static void Main(string[] args)
    {
        Func<object> f1 = ()=>null;
        Func<object> f2 = f1;  //f1与f2指向同一个对象
        object o = new object();
        f1 = ()=>o;  //更改f1后,f2仍然指向之前的对象
        bool b1 = f1() == o;   //true
        bool b2 = f2() == null;  //true,  f1()!=f2()

        Print("直接赋值:");
        Print(f1(),"f1() == {0}");
        Print(f2(),"f2() == {0}");
        Print(f1() == f2(),"f1() == f2() ? {0}");

        Func<object> ff1 = ()=>null;
        Func<object> ff2 = ()=>ff1();  //通过委托包装f1,形成闭包
        object oo = new object();
        ff1 = ()=>oo;  //更改f1后,f2与f1保持同步
        bool bb1 = ff1() == oo;  //true
        bool bb2 = ff2() == oo;  //true,  f1()==f2()

        Print("委托赋值:");
        Print(ff1(),"ff1() == {0}");
        Print(ff2(),"ff2() == {0}");
        Print(ff1() == ff2(),"ff1() == ff2() ? {0}");

        Console.ReadLine();
    }

    static void Print(object mess,string format = "{0}")
    {
        string message = mess == null ? "null" : mess.ToString();
        Console.WriteLine(string.Format(format,message));
    }
}

上面看一下SingleServiceResolver类是怎么完毕指标的创始的,该类是个泛型类,那象征能够组织任何项指标指标,不仅限于ControllerFactory,实际上在MVC中,该类在比比皆是地点都拿走了采取,例如:ControllerBuilder、DefaultControllerFactory、BuildManagerViewEngine等,完成了对各个指标的始建。

  • ViewData 是一个后续自ViewDataDictionary类的字典对象。
    public ViewDataDictionary ViewData { get; set; }
  • ViewData 用来从controller中传值到相呼应的view中。
  • 生命周期仅存在于当下此次请求。
  • 假使产生重定向,那么值将会被清空。
  • 从ViewData中取值时索要开始展览类型转换和Null Check以制止卓殊。

总结:

SingleServiceResolver

此类实现了IResolver接口,首要用以提供内定项目标实例,在SingleServiceResolver类中有三种艺术来成立对象:

1、private Lazy<TService> _currentValueFromResolver;  //内部调用_resolverThunk
2、private Func<TService> _currentValueThunk;  //委托方式
3、private TService _defaultValue;   //默认值方式

private Func<IDependencyResolver> _resolverThunk;  //IDependencyResolver方式

从Current方法中得以看来他们的事先级:

public TService Current
{
    get { return _currentValueFromResolver.Value ?? _currentValueThunk() ?? _defaultValue; }
}

_currentValueFromResolver其实是对_resolverThunk的包装,内部如故调用_resolverThunk来促成指标的结构,所以优先级是:_resolverThunk > _currentValueThunk > _defaultValue,即:IDependencyResolver方式> 委托格局 > 私下认可值形式。

Single瑟维斯Resolver在构造函数中暗中认可实现了二个DefaultDependencyResolver对象封装到委托字段_resolverThunk中,该暗中认可的Resolver是以Activator.CreateInstance(type)的不贰法门创立对象的,但是有个前提,钦命的type不可能是接口也许抽象类,不然直接再次回到null。
在ControllerBuilder类中实例化SingleServiceResolver对象的时候钦点的是IControllerFactory接口类型,所以其内部的SingleServiceResolver对象不恐怕通过IDependencyResolver格局创造对象,那么创立ControllerFactory对象的天职就完毕了_currentValueThunk(委托情势)和_defaultValue(私下认可值格局)那多少个章程上,前边说过,SingleServiceResolver类中的委托字段实际上是经过闭包引用ControllerBuilder类中的相应委托来成立对象的,而在ControllerBuilder类中,那些相应的寄托私下认可是重临null,

private Func<IControllerFactory> _factoryThunk = () => null;

所以,暗中认可景况下SingleServiceResolver类的第两种办法也失效了,那么此时也只可以凭借私下认可值方式来提供对象了,在ControllerBuilder类中这些私下认可值是DefaultControllerFactory:

internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
{
    _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
                                              () => _factoryThunk(),
                                              new DefaultControllerFactory { ControllerBuilder = this }, //默认值
                                              "ControllerBuilder.GetControllerFactory");
}

于是,在私下认可情形下是行使DefaultControllerFactory类来组织Controller的。
在开创SingleServiceResolver对象的时候,能够从八个地点判断出真正创制对象的办法是哪一种:

new SingleServiceResolver<IControllerFactory>(   //1、看泛型接口,如果为接口或抽象类,则IDependencyResolver方式失效
    () => _factoryThunk(),  //2、看_factoryThunk()是否返回null,如果是则委托方式失效
    new DefaultControllerFactory { ControllerBuilder = this },  //3、以上两种都失效,则使用该默认值
    "ControllerBuilder.GetControllerFactory");

通过上述创造对象的经过能够摸清,有两种艺术能够轮换默许的靶子提供器:

  1. 轮换暗中认可的DependencyResolver,能够透过DependencyResolver类的静态方法SetResolver方法来贯彻:

    CustomDependencyResolver customResolver = new  CustomDependencyResolver();
    DependencyResolver.SetResolver(customResolver);
    

    将上述语句放在程序运行的地点,例如:Application_Start

  2. 通过前边介绍的ControllerBuilder类的SetControllerFactory方法

注:第一种方式的先行级越来越高。

SingleServiceResolver

该类完成了IResolver接口,首要用于提供内定项指标实例,在SingleServiceResolver类中有二种办法来创立对象:

1、private Lazy<TService> _currentValueFromResolver;  //内部调用_resolverThunk
2、private Func<TService> _currentValueThunk;  //委托方式
3、private TService _defaultValue;   //默认值方式

private Func<IDependencyResolver> _resolverThunk;  //IDependencyResolver方式

从Current方法中能够看出他们的先期级:

public TService Current
{
    get { return _currentValueFromResolver.Value ?? _currentValueThunk() ?? _defaultValue; }
}

_currentValueFromResolver实际是对_resolverThunk的包裹,内部还是调用_resolverThunk来贯彻目的的布局,所以优先级是:_resolverThunk > _currentValueThunk > _defaultValue,即:IDependencyResolver格局> 委托情势 > 暗中同意值格局。

SingleServiceResolver在构造函数中默许达成了三个DefaultDependencyResolver对象封装到委托字段_resolverThunk中,该默许的Resolver是以Activator.CreateInstance(type)的主意创设对象的,不过有个前提,钦点的type不能够是接口或许抽象类,不然直接回到null。
在ControllerBuilder类中实例化SingleServiceResolver对象的时候钦命的是IControllerFactory接口类型,所以其内部的SingleServiceResolver对象不只怕透过IDependencyResolver格局创设对象,那么创建ControllerFactory对象的职务就达到了_currentValueThunk(委托格局)和_defaultValue(私下认可值格局)那五个情势上,前边说过,SingleServiceResolver类中的委托字段实际上是经过闭包引用ControllerBuilder类中的相应委托来创造对象的,而在ControllerBuilder类中,那几个相应的寄托默许是回到null,

private Func<IControllerFactory> _factoryThunk = () => null;

于是,暗中认可意况下SingleServiceResolver类的第2种办法也失效了,那么此时也只可以借助私下认可值格局来提供对象了,在ControllerBuilder类中这么些默许值是DefaultControllerFactory:

internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
{
    _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
                                              () => _factoryThunk(),
                                              new DefaultControllerFactory { ControllerBuilder = this }, //默认值
                                              "ControllerBuilder.GetControllerFactory");
}

由此,在暗中认可情状下是运用DefaultControllerFactory类来组织Controller的。
在创设SingleServiceResolver对象的时候,能够从多少个地方判断出真正创制对象的不贰诀假若哪个种类:

new SingleServiceResolver<IControllerFactory>(   //1、看泛型接口,如果为接口或抽象类,则IDependencyResolver方式失效
    () => _factoryThunk(),  //2、看_factoryThunk()是否返回null,如果是则委托方式失效
    new DefaultControllerFactory { ControllerBuilder = this },  //3、以上两种都失效,则使用该默认值
    "ControllerBuilder.GetControllerFactory");

通过以上创制对象的进度能够摸清,有二种办法能够轮换暗中认可的目的提供器:

  1. 轮换暗许的DependencyResolver,可以经过DependencyResolver类的静态方法SetResolver方法来兑现:

    CustomDependencyResolver customResolver = new  CustomDependencyResolver();
    DependencyResolver.SetResolver(customResolver);
    

    将上述语句放在程序运行的地点,例如:Application_Start

  2. 通过前面介绍的ControllerBuilder类的SetControllerFactory方法

注:第三种情势的预先级更加高。

ViewBag

一.当行使TempData对象存款和储蓄值而未调用TempData.Keep方法时,此时1经该指标被已读,然后该指标中的全体项将被标记为除去状态。

ControllerFactory

透过ControllerBuilder创制出ControllerFactory对象后,下边就要选用该目的完成具体Controller的制造,ControllerFactory都完毕了IControllerFactory接口,通过兑现CreateController艺术成功对Controller的实例化,CreateController的中间逻辑格外简单,就两步:获取Controller类型,然后创立Controller对象。

ControllerFactory

透过ControllerBuilder创立出ControllerFactory对象后,上面就要采用该指标达成具体Controller的创造,ControllerFactory都完结了IControllerFactory接口,通过兑现CreateController艺术成功对Controller的实例化,CreateController的中间逻辑相当不难,就两步:获取Controller类型,然后创制Controller对象。

  • ViewBag ViewBag是一个动态属性,是基于C# 四.0的动态语言的性格。
    public Object ViewBag { get;}
  • 是对ViewData的一次包装,也是用来从controller中传值到相呼应的view中。
  • 生命周期仅设有于当下本次请求。
  • 若是发生重定向,那么值将会被清空。
  • 从ViewBag中取值时不须要开始展览类型转换。

二.若调用TempData.Keep(string
key)方法,此时不会进行标记。

获取Controller类型

根据控制器名称获取控制器Type的长河,有至关重要深切精晓一下,以便于我们在现在遭逢相关难题的时候能够更加好的开始展览不当定位。获取项目标逻辑都封装在GetControllerType方法中,该过程依据路由数据中是或不是带有命名空间信息,分为八个阶段展开项目搜索:

  • 先是,要是当前路由数据中存在命名空间音讯,则在缓存中依照控制器名称和命名空间搜索对应的项目,若是找到唯1二个品种,则赶回该类型,找到三个一向抛卓殊
  • 其次,要是当前路由数据中不存在命名空间新闻,或在第二阶段的物色未有找到呼应的品种,并且UseNamespaceFallback==true,此时会收获ControllerBuilder中安装的命名空间音信,利用该音信和控制器名称在缓存中展开项目搜索,借使找到唯1三个品类,则赶回该品种,找到多个一贯抛十分
  • 末段,假如路由数据和ControllerBuilder中都尚无命名空间音讯,只怕在以上五个阶段都未曾检索到对应的Controller类型,那么会忽视命名空间,在缓存中仅依据控制器名称进行项目搜索,假设找到唯13个种类,则赶回该类型,找到两个一贯抛十分

为此,命名空间的优先级是:RouteData > ControllerBuilder

在缓存中搜索类型的时候,假如是第三回搜索,会调用ControllerTypeCache.EnsureInitialized方法将保存在硬盘中的Xml缓存文件加载到五个字典类型的内部存款和储蓄器缓存中。倘若该缓存文件不存在,则会遍历当前采用引用的具有程序集,找出全数public权限的Controller类型(评定规范:实现IController接口、非抽象类、类名以Controller结尾),然后将那么些类型音信实行xml种类化,生成缓存文件保留在硬盘中,以便于下次一贯从缓存文件中加载,同时将类型消息分组以字典的花样缓存在内部存款和储蓄器中,进步搜索频率,字典的key为ControllerName(不带命名空间)。

Controller类型搜索流程如下图所示:

金沙注册送58 2

获取Controller类型

基于控制器名称获取控制器Type的历程,有不可缺少深远驾驭一下,以便于大家在事后赶上相关难题的时候能够更加好的进展不当定位。获取项指标逻辑都封装在GetControllerType方法中,该进程依据路由数据中是还是不是含有命名空间音讯,分为八个等级展开项目搜索:

  • 先是,假使当前路由数量中留存命名空间消息,则在缓存中依据控制器名称和命名空间搜索对应的类型,借使找到唯1一个档次,则赶回该项目,找到两个平素抛相当
  • 其次,假设当前路由数据中不设有命名空间新闻,或在第3阶段的搜索未有找到相应的类型,并且UseNamespaceFallback==true,此时会赢得ControllerBuilder中安装的命名空间消息,利用该音讯和控制器名称在缓存中开始展览项目搜索,假如找到唯一2个档次,则赶回该类型,找到五个一贯抛卓殊
  • 提起底,借使路由数据和ControllerBuilder中都未曾命名空间音讯,可能在上述四个阶段都并未有寻找到相应的Controller类型,那么会忽略命名空间,在缓存中仅根据控制器名称进行项目搜索,假设找到唯一七个类型,则赶回该类型,找到多个一贯抛卓殊

故而,命名空间的事先级是:RouteData > ControllerBuilder

在缓存中找找类型的时候,假若是率先次搜索,会调用ControllerTypeCache.EnsureInitialized方法将保留在硬盘中的Xml缓存文件加载到三个字典类型的内存缓存中。若是该缓存文件不设有,则会遍历当前利用引用的全部程序集,找出装有public权限的Controller类型(认清标准:实现IController接口、非抽象类、类名以Controller结尾),然后将这个类型音讯实行xml连串化,生成缓存文件保留在硬盘中,以便于下次径直从缓存文件中加载,同时将类型消息分组以字典的样式缓存在内部存款和储蓄器中,升高搜索频率,字典的key为ControllerName(不带命名空间)。

Controller类型搜索流程如下图所示:

金沙注册送58 3

TempData

三.RedirectToRouteResult和RedirectResult总是会调用TempData.Keep()方法,有限援救该对象中的全部项不会被移除。

创建Controller对象

取得Controller类型现在,接下去就要实行Controller对象的创办。在DefaultControllerFactory类的源码中得以见见,同ControllerBuilder类似,该类的构造函数中也实例化了1个SingleServiceResolver对象,根据事先介绍的法子,大家一眼就能够见到,该目的是利用暗中同意值的点子提供了八个DefaultControllerActivator对象。

_activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>(  //1、泛型为接口,IDependencyResolver方式失效
                     () => null,  //2、返回了null,委托方式失效
                     new DefaultControllerActivator(dependencyResolver),  //3、以上两种方式均失效,则使用该提供方式
                     "DefaultControllerFactory constructor");

实则DefaultControllerFactory类仅完成了体系的查找,对象的实在制造进度必要由DefaultControllerActivator类来成功,默许情况下,DefaultControllerActivator创立Controller的长河是相当粗略的,因为它实际上采用的是二个叫做DefaultDependencyResolver的类来进展Controller创设的,在此类内部一向调用Activator.CreateInstance(serviceType)主意成功目的的实例化。

从DefaultControllerFactory和DefaultControllerActivator那三个类的开创进程能够发现,MVC提供了两种主意(IDependencyResolver方式、委托方式、暗许值格局)来提供对象,因而在对MVC相关模块实行扩充的时候,也有三种方式得以运用。

创建Controller对象

收获Controller类型以往,接下去就要举办Controller对象的创办。在DefaultControllerFactory类的源码中得以见见,同ControllerBuilder类似,该类的构造函数中也实例化了3个SingleServiceResolver对象,根据事先介绍的主意,大家1眼就能够看出,该指标是采取默许值的章程提供了贰个DefaultControllerActivator对象。

_activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>(  //1、泛型为接口,IDependencyResolver方式失效
                     () => null,  //2、返回了null,委托方式失效
                     new DefaultControllerActivator(dependencyResolver),  //3、以上两种方式均失效,则使用该提供方式
                     "DefaultControllerFactory constructor");

实则DefaultControllerFactory类仅达成了花色的摸索,对象的真的创设进程必要由DefaultControllerActivator类来完结,暗中认可景况下,DefaultControllerActivator创制Controller的进度是非常的粗略的,因为它实际采纳的是1个誉为DefaultDependencyResolver的类来展开Controller创造的,在此类内部一贯调用Activator.CreateInstance(serviceType)艺术成功目的的实例化。

从DefaultControllerFactory和DefaultControllerActivator那多个类的开创进度能够窥见,MVC提供了七种方法(IDependencyResolver格局、委托方式、暗中认可值情势)来提供对象,由此在对MVC相关模块举办扩大的时候,也有多种艺术能够利用。

  • TempData
    是二个后续于TempDataDictionary类的字典对象,存款和储蓄于Session中 。
    public TempDataDictionary TempData { get; set; }
  • TempData 用来展开跨页面请求传值。
  • TempData被呼吁后生命周期即甘休。
  • 从TempData中取值时供给举办类型转换和Null Check以幸免极度。
  • 根本用来储存3次性数据新闻,比如error messages, validation
    messages。
    详情可参照:TempData知多少,
    Session
  • ASP.NET
    MVC中Session是Controller中的壹本性质,Session是HttpSessionStateBase类型。
    public HttpSessionStateBase Session { get; }
  • Session保存数据直到用户会话结束(暗中认可session过期时间为20mins)。
  • Session对具备的请求都有效,不仅仅是单一的跳转。
  • 从Session中取值时索要进行类型转换和Null Check以制止卓殊。

Controller中的数据容器

Controller中提到到多少个给view传值的数码容器:TempData、ViewData和ViewBag。前两者的不一致之处在于TempData仅存款和储蓄权且数据,里面包车型客车多寡在首先次读取之后会被移除,即:只好被读取3遍;ViewData和ViewBag保存的是同等份数据,只不过ViewBag是动态目的,对ViewData进行了包装。

public dynamic ViewBag
{
    get
    {
        if (_dynamicViewDataDictionary == null)
        {
            _dynamicViewDataDictionary = new DynamicViewDataDictionary(() => ViewData); //封装ViewData
        }
        return _dynamicViewDataDictionary;
    }
}  

下边简单说一下TempData的贯彻原理。

Controller中的数据容器

Controller中关系到多少个给view传值的数据容器:TempData、ViewData和ViewBag。前两者的分裂之处在于TempData仅存款和储蓄权且数据,里面包车型地铁数额在第2回读取之后会被移除,即:只好被读取三回;ViewData和ViewBag保存的是一模1样份数据,只可是ViewBag是动态指标,对ViewData进行了包装。

public dynamic ViewBag
{
    get
    {
        if (_dynamicViewDataDictionary == null)
        {
            _dynamicViewDataDictionary = new DynamicViewDataDictionary(() => ViewData); //封装ViewData
        }
        return _dynamicViewDataDictionary;
    }
}  

上边简单说一下TempData的兑现原理。


TempData

首先看下MSDN上是怎么样解释的:

你能够按使用 ViewDataDictionary 对象的等同方法采纳 TempDataDictionary
对象传递数据。 可是,TempDataDictionary
对象中的数据仅从三个伸手保持到下一个请求,除非你利用 Keep
方法将一个或七个键标记为需保存。
假若键已标记为需保留,则会为下一个伸手保留该键。
TempDataDictionary
对象的卓著用法是,在数量重定向到两个操作方法时从另贰个操作方法传递数据。
例如,操作方法恐怕会在调用 RedirectToAction
方法以前,将有关错误的音信囤积在控制器的 TempData 属性(该属性再次回到TempDataDictionary 对象)中。
然后,下3个操作方法能够处理错误并显现展现错误消息的视图。

TempData的天性便是足以在多个Action之间传递数据,它会保留一份数据到下二个Action,并乘胜再下1个Action的赶到而失效。所以它被用在两个Action之间来保存数据,比如,那样二个情景,你的二个Action接受部分post的数额,然后交由另2个Action来拍卖,并呈现到页面,那时就足以行使TempData来传递那份数据。

TempData完成了IDictionary接口,同时内部含有三个IDictionary类型的个体字段,并添加了有关办法对字典字段的操作实行了决定,那鲜明是代理格局的一个行使。因为TempData必要在Action之间传递数据,因而须求其能够对本人的数额开始展览封存,TempData依赖ITempDataProvider接口达成了数量的加载与保留,暗中认可情状下是选拔SessionStateTempDataProvider对象将TempData中的数据存放在Session中。

上边看一下TempData是何等控制数据操作的,TempDataDictionary源码中有如此壹段定义:

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

私有字典字段_data是的确存放数据的地点,哈希集合_initialKeys和_retainedKeys用来标记数据,_initialKeys中存放尚未被读取的数码key,_retainedKeys存放能够被1再走访的key。
TempDataDictionary对数码操作的控制作为根本呈今后在读取数据的时候并不会及时从_data中除去相应的数目,而是经过_initialKeys和_retainedKeys那三个hashset标记每条数据的情况,最终在经过ITempDataProvider进行保存的时候再遵照以前标记的情事对数码实行过滤,那时才去除已走访过的多寡。

连锁的操纵方式有:TryGetValue、Add、Keep、Peek、Remove、Clear

1、TryGetValue

public bool TryGetValue(string key, out object value)
{
    _initialKeys.Remove(key);
    return _data.TryGetValue(key, out value);
}

该方法在读取数据的时候,会从_initialKeys集合中移除对应的key,前边说过,因为_initialKeys是用来标记数据未访问状态的,从该集合中删去了key,之后在经过ITempDataProvider保存的时候就会将数据从_data字典中除去,下1回呼吁就无法再从TempData访问该key对应的数额了,即:数据只可以在三次呼吁中选择。

2、Add

public void Add(string key, object value)
{
    _data.Add(key, value);
    _initialKeys.Add(key);
}

加上数据的时候在_initialKeys中打上标记,注解该key对应的数量能够被访问。

3、Keep

public void Keep(string key)
{
    _retainedKeys.Add(key);
} 

调用Keep方法的时候,会将key添加到_retainedKeys中,表明该条记录能够被频仍造访,为何能够被反复走访呢,能够从Save方法中找到原因:

public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
{
    // Frequently called so ensure delegate is stateless
    _data.RemoveFromDictionary((KeyValuePair<string, object> entry, TempDataDictionary tempData) =>
        {
            string key = entry.Key;
            return !tempData._initialKeys.Contains(key) 
                && !tempData._retainedKeys.Contains(key);
        }, this);

    tempDataProvider.SaveTempData(controllerContext, _data);
}

能够看出,在保留的时候,会从_data中取出每一条数据,判断该数额的key是不是存在于_initialKeys和_retainedKeys中,即使都不存在才会从_data中移除,所以keep方法将key添加到_retainedKeys后,该数据就不会被删去了,即:能够在四个请求中被访问了。

4、Peek

public object Peek(string key)
{
    object value;
    _data.TryGetValue(key, out value);
    return value;
}

从代码中得以看到,该措施在读取数据的时候,仅仅是从_data中实行了收获,并不曾移除_initialKeys集合中对应的key,由此通过该办法读取数据不影响多少的情景,该条数据依旧得以在下三回呼吁中被运用。

5、Remove 与 Clear

public bool Remove(string key)
{
    _retainedKeys.Remove(key);
    _initialKeys.Remove(key);
    return _data.Remove(key);
}

public void Clear()
{
    _data.Clear();
    _retainedKeys.Clear();
    _initialKeys.Clear();
}

那多少个点子没什么多说的,只是在剔除数据的时候还要删除其相应的事态。

TempData

先是看下MSDN上是怎么分解的:

你能够按使用 ViewDataDictionary 对象的均等方法使用 TempDataDictionary
对象传递数据。 可是,TempDataDictionary
对象中的数据仅从1个伸手保持到下三个请求,除非您利用 Keep
方法将一个或多个键标记为需保留。
就算键已标记为需保存,则会为下2个呼吁保留该键。
TempDataDictionary
对象的优秀用法是,在数额重定向到贰个操作方法时从另一个操作方法传递数据。
例如,操作方法大概会在调用 RedirectToAction
方法以前,将有关错误的新闻囤积在控制器的 TempData 属性(该属性重临TempDataDictionary 对象)中。
然后,下二个操作方法能够处理错误并显现突显错误信息的视图。

TempData的风味就是能够在五个Action之间传递数据,它会保留一份数据到下三个Action,并趁机再下一个Action的赶来而失效。所以它被用在多个Action之间来保存数据,比如,那样3个场景,你的2个Action接受部分post的数据,然后交由另1个Action来处理,并呈现到页面,那时就足以应用TempData来传递那份数据。

TempData完结了IDictionary接口,同时内部含有三个IDictionary类型的私有字段,并添加了相关方法对字典字段的操作进行了决定,那明显是代理方式的三个选取。因为TempData须求在Action之间传递数据,由此供给其能够对自家的数目开展封存,TempData正视ITempDataProvider接口完结了数量的加载与保留,暗中同意情形下是运用SessionStateTempDataProvider对象将TempData中的数据存放在Session中。

上边看一下TempData是怎么控制数据操作的,TempDataDictionary源码中有如此①段定义:

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

私有字典字段_data是实在存放数据的地点,哈希集合_initialKeys和_retainedKeys用来标记数据,_initialKeys中存放尚未被读取的数目key,_retainedKeys存放能够被反复访问的key。
TempDataDictionary对数据操作的支配行为重点反映在在读取数据的时候并不会登时从_data中删除相应的数目,而是通过_initialKeys和_retainedKeys这多少个hashset标记每条数据的情景,最后在通过ITempDataProvider举办封存的时候再依照此前标记的动静对数据开始展览过滤,那时才去除已走访过的多寡。

连锁的决定措施有:TryGetValue、Add、Keep、Peek、Remove、Clear

1、TryGetValue

public bool TryGetValue(string key, out object value)
{
    _initialKeys.Remove(key);
    return _data.TryGetValue(key, out value);
}

该情势在读取数据的时候,会从_initialKeys集合中移除对应的key,后边说过,因为_initialKeys是用来标记数据未访问状态的,从该集合中剔除了key,之后在经过ITempDataProvider保存的时候就会将数据从_data字典中除去,下三回呼吁就不可能再从TempData访问该key对应的数据了,即:数据只还好2遍呼吁中应用。

2、Add

public void Add(string key, object value)
{
    _data.Add(key, value);
    _initialKeys.Add(key);
}

丰硕数码的时候在_initialKeys中打上标记,表明该key对应的数额能够被访问。

3、Keep

public void Keep(string key)
{
    _retainedKeys.Add(key);
} 

调用Keep方法的时候,会将key添加到_retainedKeys中,阐明该条记录能够被频仍拜访,为啥能够被反复走访呢,能够从Save方法中找到原因:

public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
{
    // Frequently called so ensure delegate is stateless
    _data.RemoveFromDictionary((KeyValuePair<string, object> entry, TempDataDictionary tempData) =>
        {
            string key = entry.Key;
            return !tempData._initialKeys.Contains(key) 
                && !tempData._retainedKeys.Contains(key);
        }, this);

    tempDataProvider.SaveTempData(controllerContext, _data);
}

能够见见,在保存的时候,会从_data中取出每一条数据,判断该数额的key是不是存在于_initialKeys和_retainedKeys中,要是都不存在才会从_data中移除,所以keep方法将key添加到_retainedKeys后,该多少就不会被删去了,即:能够在多少个请求中被访问了。

4、Peek

public object Peek(string key)
{
    object value;
    _data.TryGetValue(key, out value);
    return value;
}

从代码中得以观察,该措施在读取数据的时候,仅仅是从_data中展开了得到,并未移除_initialKeys集合中对应的key,因而通过该办法读取数据不影响多少的状态,该条数据仍旧得以在下一次呼吁中被使用。

5、Remove 与 Clear

public bool Remove(string key)
{
    _retainedKeys.Remove(key);
    _initialKeys.Remove(key);
    return _data.Remove(key);
}

public void Clear()
{
    _data.Clear();
    _retainedKeys.Clear();
    _initialKeys.Clear();
}

那八个措施没什么多说的,只是在剔除数据的时候还要删除其相应的景况。

Q51. 怎么样持久化TempData?
Ans. TempData的生命周期十一分急促,只好存活到指标视图完全加载之后。
只是大家得以透过调用Keep方法去持久化TempData至下3回访问。

  • void Keep() –
    调用那么些艺术将保障此番请求之后有所的TempData都将会被持久化。

    public ActionResult Index()
    {
     ViewBag.Message = TempData["Message"];
     Employee emp = TempData["emp"] as Employee; //need type casting
     TempData.Keep();//persist all strings values
     return View();
    }
    
  • void Keep(string key) –
    调用这么些主意将保证本次请求之后钦赐的TempData会被持久化。

    public ActionResult Index()
    {
     ViewBag.Message = TempData["Message"];
     Employee emp = TempData["emp"] as Employee; //need type casting
     //persist only data for emp key and Message key will be destroy
     TempData.Keep("emp");
     return View();
    }
    

Q5二. ASP.NET MVC中怎么样决定session的行事?
Ans. 默许ASP.NET MVC 协助 session state(会话状态).
Session用来囤积跨请求
中间的数码。 不管你是还是不是在session中存款和储蓄数据,ASP.NET
MVC都不可能不为富有的controller管理 session state,且是耗费时间的
。因而session是储存在劳务器端的,消耗服务器的内部存款和储蓄器,所以一定影响你的应用程序的质量。
借使您的一点controller不必要session控制,能够手动关闭session控制,来充实微小的属性升高。
可以透过 session state的配置项来简化它。
ASP.NET
MVC4中的SessionState个性中,能够透过点名SessionStateBehavior枚举来达成越来越多对session-state的主宰。

  • Default :暗许的session state控制情势。
  • Disabled: Session state完全关闭。
  • ReadOnly:只读的session state。
  • Required:完全的可读写的 session state。

金沙注册送58 4


Q伍三. ASP.NET MVC中 TempData与Session 有啥关系关系?
Ans. ASP.NET
MVC中TempData使用session存款和储蓄跨请求的一时数据。由此,当您关闭了controller的session,当您去行使TempData时,就会抛出以下格外。
金沙注册送58 5


Q5四. ASP.NET MVC中怎样是Action方法?
Ans.
Controller中的action是概念在Controller类中的方法用来推行基于用户请求的操作,并在Model的扶植下将结果传递会View。
Asp.net MVC 中融为1体了以下三种ActionResults类型及相应的鼎力相助类情势:

  1. ViewResult –
    使用Controller中提供的View()主意再次来到五个ViewResult用来展现钦命或暗中同意的View。
  2. PartialViewResult-
    使用Controller中提供的PartialView()艺术重回三个PartialViewResult用来彰显内定或暗许的分部视图。
  3. RedirectResult –
    使用Controller中提供的Redirect()方法重返一个RedirectResult用来倡导2个HTTP 301 或 30二 到钦赐U汉兰达L的跳转。
  4. RedirectToRouteResult –
    使用Controller中提供的RedirectToAction(), RedirectToActionPermanent(), RedirectToRoute(), RedirectToRoutePermanent()措施重临三个RedirectToRouteResult用来倡导贰个HTTP 301或 30二 到内定action或许路由的跳转。
  5. ContentResult –
    使用Controller中提供的Content()方法重返一个ContentResult用来显现钦定的文书。
  6. JsonResult –
    使用Controller中提供的Json()办法再次来到三个JsonResult用来显示种类化的Json格式数据。
  7. JavaScriptResult –
    使用Controller中提供的JavaScript()主意重临三个JavaScriptResult用来彰显一段JavaScript代码,一般仅用于Ajax请求的光景。
  8. FileResult –
    使用Controller中提供的File()格局再次回到二个FileResult用来显示文件(PDF,
    DOC, Excel等)内容。
  9. EmptyResult – 重回二个空的结果。
  10. 金沙注册送58 ,HttpNotFoundResult –
    使用Controller中提供的HttpNotFound()情势再次回到一个HTTP 40四动静。
  11. HttpUnauthorizedResult –
    再次来到一个HttpUnauthorizedResult类型用来表示HTTP
    40壹景况(未证实)。用来供给用户登录以成就认证。
  12. HttpStatusCodeResult – 重返HttpStatusCodeResult用来代表内定Http状态。

Q5陆. ASP.NET MVC中哪些标记Non-Action方法?
Ans. ASP.NET MVC 将装有的集体方法暗中认可为action方法。
假设不想有些公共的点子被记者爆料光为Action,仅须要用NonActionAttribute标志方法即可。

[NonAction]
public void DoSomething()
{
 // Method logic
}

Q5七. 能无法更改Action方法的命名?
Ans.
能够经过ActionName特征来修改Action的命名。修改后Action将用ActionName中定义的称呼被调用。

[ActionName("DoAction")]
public ActionResult DoSomething()
{
 //TODO:
 return View();
}

这样,DoSomething action就会被会被标记为DoAction action。


Q5八. 如何限制action仅能被相应的HTTP GET, POST, PUT or DELETE请求访问?
Ans. 默许,每3个action方法都得以被其它HTTP请求访问(i.e. GET, PUT,
POST,
DELETE). 可是能够透过为action方法内定HttpPost、 HttpPut 、 HttpDelete
本性来限制action的行为。

[HttpGet]
public ActionResult Index()
{
 //TODO:
 return View();
}

Q5玖. 怎么样决定贰个action是被HTTP GET照旧POST请求?
Ans.
通过使用HttpRequestBase类的HttpMethod质量能够判断action是被哪一种HTTP请求调用。

public ActionResult Index(int? id)
{
 if (Request.HttpMethod == "GET")
 {
 //TODO:
 }
 else if (Request.HttpMethod == "POST")
 {
 //TODO:
 }
 else
 {
 //TODO:
 }
return View();
}

Q60. 怎样判定三个AJAX请求?
Ans. 通过利用Request.IsAjaxRequest()来判断。

public ActionResult DoSomething()
{
 if (Request.IsAjaxRequest())
 {
 //TODO:
 }
 return View();
}

相关文章

网站地图xml地图