关于MVC中TempData持久化难点

如今在做mvc跨控制器传值的时候发现3个标题,就是偶尔候TempData的值为null,然后查阅了累累素材,发现了累累都以逻辑和公理什么的(想看规律能够查阅原理的篇章,本文是用法),不过真正消除的不二等秘书诀怎么案例都未曾,

近日抽空看了刹那间ASP.NET
MVC的壹部分源码,顺带写篇文章做个笔记以便日后查看。

近年忙里偷闲看了须臾间ASP.NET
MVC的有的源码,顺带写篇文章做个笔记以便日后查看。

本类别重要翻译自《ASP.NET MVC Interview Questions and Answers 》- By
Shailendra
Chauhan,想看英文原版的可访问机关下载。该书首要分为两部分,ASP.NET
MVC 五、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。
其处理逻辑首要完成在1块和异步的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
中能够在三回用户会话中接纳Session去持久化数据。

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

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

里头第二步在ProcessRequestInit方法中开始展览处理,本文首要是分析第两步中的controller是怎么着成立出来的。

中间第2步在ProcessRequestInit方法中进行拍卖,本文首尽管分析第两步中的controller是哪些创设出来的。

美高梅开户网址 1

在日前Action方法中调用Keep方法则保障在当前央浼中TempData对象中所存款和储蓄的键都不会被移除。

Controller的始建是由此ControllerFactory完结的,而ControllerFactory的创导又是在ControllerBuilder中完毕的,由此大家先理解一下ControllerBuilder的做事原理。

Controller的创制是经过ControllerFactory达成的,而ControllerFactory的创建又是在ControllerBuilder中完毕的,由此我们先精通一下ControllerBuilder的做事原理。

ViewData

 

ControllerBuilder

从源码中能够看看,在ControllerBuilder类中,并不曾一贯促成对controller工厂的创制,ControllerFactory的创建实际上是寄托给2个后续自IResolver接口的SingleServiceResolver类的实例来落实的,那一点从GetControllerFactory方法中能够看出,它是通过调用SingleServiceResolver对象的Current属性来形成controller工厂的创导的。

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

再者在源码中还发现,SingleServiceResolver类是internal级其余,那表示外部无法直接待上访问,那么ControllerBuilder是怎样借助SingleServiceResolver来完毕工厂的注册呢?继续看代码,ControllerBuilder类和Single瑟维斯Resolver类都有二个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类中才能达成真正的挂号,大家领略,倘使是不过的引用赋值,那么更改贰个引用并不会对别的二个引用造成改变,比如:

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对象中),而是通过委托来进展了打包,这样就会形成3个闭包,在闭包中进行引用,如下所示:

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

下边看一下SingleServiceResolver类是什么兑现目标的创制的,该类是个泛型类,那象征能够组织任何项指标目的,不仅限于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类都有2个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类中才能落到实处真正的注册,我们精通,假若是独自的引用赋值,那么更改一个引用并不会对其余3个引用造成改变,比如:

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

关于MVC中TempData持久化难点。如此那般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));
    }
}

上边看一下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方式> 委托格局 > 暗中同意值格局。

SingleServiceResolver在构造函数中暗许实现了1个DefaultDependencyResolver对象封装到委托字段_resolverThunk中,该暗中认可的Resolver是以Activator.CreateInstance(type)的法子创立对象的,不过有个前提,钦点的type不可能是接口可能抽象类,不然直接重临null。
在ControllerBuilder类中实例化SingleServiceResolver对象的时候内定的是IControllerFactory接口类型,所以个中间的SingleServiceResolver对象不能透过IDependencyResolver情势成立对象,那么创立ControllerFactory对象的天职就达到了_currentValueThunk(委托情势)和_defaultValue(暗中认可值情势)那五个办法上,前边说过,Single瑟维斯Resolver类中的委托字段实际上是通过闭包引用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的。
在开立Single瑟维斯Resolver对象的时候,能够从八个地方判断出真正创立对象的格局是哪个种类:

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接口,首要用于提供内定项目标实例,在Single瑟维斯Resolver类中有两种艺术来创设对象:

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在构造函数中暗许完成了3个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的。
在创立Single瑟维斯Resolver对象的时候,能够从多少个地点判断出真正创建对象的主意是哪一种:

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

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

ControllerFactory

因而ControllerBuilder创设出ControllerFactory对象后,下边就要选拔该对象达成具体Controller的创办,ControllerFactory都落到实处了IControllerFactory接口,通过落到实处CreateController主意成功对Controller的实例化,CreateController的中间逻辑相当容易,就两步:获取Controller类型,然后成立Controller对象。

ControllerFactory

经过ControllerBuilder创制出ControllerFactory对象后,上边就要采用该对象完毕具体Controller的创建,ControllerFactory都落到实处了IControllerFactory接口,通过完成CreateController方法成功对Controller的实例化,CreateController的内部逻辑十分简单,就两步:获取Controller类型,然后制造Controller对象。

  • ViewBag ViewBag是3个动态属性,是基于C# 四.0的动态语言的表征。
    public Object ViewBag { get;}
  • 是对ViewData的二次包装,也是用来从controller中传值到相呼应的view中。
  • 生命周期仅存在于方今本次请求。
  • 设若发生重定向,那么值将会被清空。
  • 从ViewBag中取值时不须求进行类型转换。

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

获取Controller类型

遵照控制器名称获取控制器Type的进度,有必不可缺深远驾驭一下,以便于大家在之后遇上相关题材的时候能够更好的拓展不当定位。获取项目标逻辑都封装在GetControllerType方法中,该进程依据路由数据中是还是不是带有命名空间音讯,分为多个阶段举办项目搜索:

  • 先是,要是当前路由数据中设有命名空间音信,则在缓存中遵照控制器名称和命名空间搜索对应的类型,假若找到唯壹3个品种,则赶回该项目,找到八个一贯抛相当
  • 附带,要是当前路由数据中不设有命名空间音讯,或在首先品级的摸索未有找到呼应的种类,并且UseNamespaceFallback==true,此时会取得ControllerBuilder中设置的命名空间音信,利用该音讯和控制器名称在缓存中开始展览项目搜索,假设找到唯一三个类型,则赶回该品种,找到五个一直抛万分
  • 最终,如若路由数据和ControllerBuilder中都并未有命名空间信息,只怕在以上多个级次都未曾检索到对应的Controller类型,那么会忽视命名空间,在缓存中仅遵照控制器名称进行项目搜索,若是找到唯一1个品种,则赶回该项目,找到多少个从来抛卓殊

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

在缓存中摸索类型的时候,假设是第一遍搜索,会调用ControllerTypeCache.EnsureInitialized方法将保留在硬盘中的Xml缓存文件加载到贰个字典类型的内部存款和储蓄器缓存中。假设该缓存文件不设有,则会遍历当前利用引用的兼具程序集,找出全部public权限的Controller类型(看清标准:完结IController接口、非抽象类、类名以Controller结尾),然后将那么些类型音信举办xml种类化,生成缓存文件保留在硬盘中,以便于下次直接从缓存文件中加载,同时将类型音讯分组以字典的款型缓存在内部存储器中,提升搜索频率,字典的key为ControllerName(不带命名空间)。

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

美高梅开户网址 2

获取Controller类型

据他们说控制器名称获取控制器Type的历程,有要求浓厚摸底一下,以便于大家在未来赶上相关题材的时候能够更好的实行不当定位。获取项指标逻辑都封装在GetControllerType方法中,该进程依照路由数据中是还是不是包罗命名空间消息,分为八个级次展开项目搜索:

  • 第2,假若当前路由数量中留存命名空间音讯,则在缓存中依据控制器名称和命名空间搜索对应的品类,假若找到唯一一个项目,则赶回该类型,找到多个平素抛万分
  • 协理,如若当前路由数量中不存在命名空间新闻,或在首先等级的寻找没有找到相应的体系,并且UseNamespaceFallback==true,此时会拿走ControllerBuilder中装置的命名空间新闻,利用该消息和控制器名称在缓存中进行项目搜索,假使找到唯壹二个品类,则赶回该项目,找到五个一贯抛相当
  • 最终,借使路由数据和ControllerBuilder中都从未有过命名空间音讯,恐怕在上述五个阶段都未曾检索到相应的Controller类型,那么会忽视命名空间,在缓存中仅根据控制器名称举办项目搜索,借使找到唯1多个门类,则赶回该品种,找到多少个一向抛非凡

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

在缓存中搜索类型的时候,假使是首先次搜索,会调用ControllerTypeCache.EnsureInitialized方法将保留在硬盘中的Xml缓存文件加载到3个字典类型的内部存款和储蓄器缓存中。假设该缓存文件不设有,则会遍历当前选用引用的装有程序集,找出富有public权限的Controller类型(判定标准:达成IController接口、非抽象类、类名以Controller结尾),然后将这个类型音信进行xml种类化,生成缓存文件保留在硬盘中,以便于下次一向从缓存文件中加载,同时将类型信息分组以字典的款型缓存在内部存款和储蓄器中,提升搜索频率,字典的key为ControllerName(不带命名空间)。

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

美高梅开户网址 3

TempData

3.RedirectToRouteResult和RedirectResult总是会调用TempData.Keep()方法,保障该指标中的全部项不会被移除。

创建Controller对象

获取Controller类型以往,接下去就要拓展Controller对象的创始。在DefaultControllerFactory类的源码中能够看看,同ControllerBuilder类似,该类的构造函数中也实例化了贰个SingleServiceResolver对象,依照以前介绍的法子,大家1眼就可以观望,该对象是应用私下认可值的办法提供了1个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类似,该类的构造函数中也实例化了三个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相关模块举办增加的时候,也有种种格局得以接纳。

  • TempData
    是一个一而再于TempDataDictionary类的字典对象,存款和储蓄于Session中 。
    public TempDataDictionary TempData { get; set; }
  • TempData 用来举行跨页面请求传值。
  • TempData被呼吁后生命周期即结束。
  • 从TempData中取值时索要展开类型转换和Null Check以幸免极度。
  • 首要用来存款和储蓄叁次性数据新闻,比如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仅存款和储蓄目前数据,里面包车型大巴数目在率先次读取之后会被移除,即:只可以被读取三遍;ViewData和ViewBag保存的是千篇一律份数据,只可是ViewBag是动态目的,对ViewData进行了包装。

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

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


TempData

首先看下MSDN上是怎么着诠释的:

您能够按使用 ViewDataDictionary 对象的如出壹辙方法接纳 TempDataDictionary
对象传递数据。 可是,TempDataDictionary
对象中的数据仅从一个请求保持到下一个请求,除非您利用 Keep
方法将二个或多个键标记为需保存。
假诺键已标记为需保留,则会为下一个呼吁保留该键。
TempDataDictionary
对象的优异用法是,在数码重定向到3个操作方法时从另一个操作方法传递数据。
例如,操作方法或然会在调用 RedirectToAction
方法在此之前,将关于错误的新闻存储在控制器的 TempData 属性(该属性重返TempDataDictionary 对象)中。
然后,下3个操作方法能够处理错误并显现显示错误新闻的视图。

TempData的表征便是能够在多个Action之间传递数据,它会保留一份数据到下叁个Action,并趁机再下三个Action的到来而失效。所以它被用在三个Action之间来保存数据,比如,那样贰个景观,你的1个Action接受一些post的多少,然后提交另二个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字典中删除,下2回呼吁就相当的小概再从TempData访问该key对应的数目了,即:数据只还好3回呼吁中使用。

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中,申明该条记录能够被一再做客,为啥能够被1再访问呢,能够从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
对象中的数据仅从二个请求保持到下2个呼吁,除非你利用 Keep
方法将叁个或八个键标记为需保留。
如若键已标记为需保存,则会为下二个请求保留该键。
TempDataDictionary
对象的超人用法是,在多少重定向到2个操作方法时从另贰个操作方法传递数据。
例如,操作方法恐怕会在调用 RedirectToAction
方法在此以前,将有关错误的音讯存储在控制器的 TempData 属性(该属性再次来到TempDataDictionary 对象)中。
然后,下1个操作方法能够处理错误并显现展现错误新闻的视图。

TempData的表征正是足以在多少个Action之间传递数据,它会保留一份数据到下八个Action,并随着再下多个Action的来临而失效。所以它被用在多个Action之间来保存数据,比如,那样一个气象,你的多个Action接受部分post的数额,然后交到另一个Action来处理,并体现到页面,那时就能够使用TempData来传递那份数据。

TempData完成了IDictionary接口,同时中间含有3个IDictionary类型的私家字段,并添加了连带措施对字典字段的操作进行了决定,那鲜明是代理格局的叁个选用。因为TempData须要在Action之间传递数据,因而必要其能够对自个儿的数额举行封存,TempData重视ITempDataProvider接口达成了数量的加载与封存,默许意况下是行使SessionStateTempDataProvider对象将TempData中的数据存放在Session中。

下边看一下TempData是哪些控制数据操作的,TempDataDictionary源码中有如此1段定义:

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

这八个主意没什么多说的,只是在剔除数据的时候还要删除其相应的情景。

Q5一. 怎样持久化TempData?
Ans. TempData的生命周期相当短暂,只好存活到指标视图完全加载之后。
可是大家能够通过调用Keep方法去持久化TempData至下一回访问。

  • 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。

美高梅开户网址 4


Q伍三. ASP.NET MVC中 TempData与Session 有何样关系关系?
Ans. ASP.NET
MVC中TempData使用session存款和储蓄跨请求的临时数据。由此,当您关闭了controller的session,当你去选用TempData时,就会抛出以下很是。
美高梅开户网址 5


Q54. 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()方法再次来到3个RedirectResult用来倡导一个HTTP 30一 或 302 到钦定URAV4L的跳转。
  4. RedirectToRouteResult –
    使用Controller中提供的RedirectToAction(), RedirectToActionPermanent(), RedirectToRoute(), RedirectToRoutePermanent()方法重临2个RedirectToRouteResult用来倡导叁个HTTP 30一或 30二 到钦赐action恐怕路由的跳转。
  5. ContentResult –
    使用Controller中提供的Content()方法重返二个ContentResult用来显示钦赐的文件。
  6. JsonResult –
    使用Controller中提供的Json()艺术再次回到二个JsonResult用来显现体系化的Json格式数据。
  7. JavaScriptResult –
    使用Controller中提供的JavaScript()措施重返八个JavaScriptResult用来表现1段JavaScript代码,一般仅用于Ajax请求的场景。
  8. FileResult –
    使用Controller中提供的File()措施重回四个FileResult用来显示文件(PDF,
    DOC, Excel等)内容。
  9. EmptyResult – 重回贰个空的结果。
  10. HttpNotFoundResult –
    使用Controller中提供的HttpNotFound()格局再次来到三个HTTP 40肆情况。
  11. HttpUnauthorizedResult –
    再次回到3个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
}

Q五柒. 能还是不能够更改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. 默许,每三个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地图