威尼斯人线上娱乐

有关MVC中TempData持久化难点

4 4月 , 2019  

近期在做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

有关MVC中TempData持久化难点。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();
        }

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

在那之中第二步在ProcessRequestInit方法中开始展览拍卖,本文主假如分析第两步中的controller是如何创设出来的。

威尼斯人线上娱乐 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类的厂子委托字段,并从未更改Single瑟维斯Resolver类的厂子委托字段,

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

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

于是必须将相应的改变应用到Single瑟维斯Resolver类中才能完成真正的挂号,我们驾驭,假若是可是的引用赋值,那么更改2个引用并不会对此外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方法注册的寄托不恐怕使用到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");
}

诸如此类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的创办实际上是寄托给2个继续自IResolver接口的SingleServiceResolver类的实例来完成的,这点从GetControllerFactory方法中得以看出,它是透过调用SingleServiceResolver对象的Current属性来形成controller工厂的创导的。

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

并且在源码中还发现,SingleServiceResolver类是internal级其余,那代表外部不可能直接待上访问,那么ControllerBuilder是怎样依靠SingleServiceResolver来完毕工厂的注册呢?继续看代码,ControllerBuilder类和SingleServiceResolver类都有1个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在实例化Single瑟维斯Resolver对象的时候,并未将小编的厂子委托字段直接赋值给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");
}

诸如此类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等,完结了对各类目的的开创。

  • ViewData 是2个持续自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在构造函数中私下认可达成了一个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;

故此,暗中同意景况下Single瑟维斯Resolver类的第三种办法也失效了,那么此时也只能注重默许值格局来提供对象了,在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方法

注:第3种办法的预先级更高。

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在构造函数中私下认可完结了1个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方法

注:第三种方法的预先级更高。

ViewBag

①.当用到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是多少个动态属性,是基于C# 4.0的动态语言的个性。
    public Object ViewBag { get;}
  • 是对ViewData的二遍包装,也是用来从controller中传值到相对应的view中。
  • 生命周期仅设有于当下本次请求。
  • 设若爆发重定向,那么值将会被清空。
  • 从ViewBag中取值时不要求展开类型转换。

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

获取Controller类型

基于控制器名称获取控制器Type的进度,有不可缺少深远通晓一下,以便于大家在此后遇见相关题材的时候能够更好的展开不当定位。获取项目标逻辑都封装在GetControllerType方法中,该进度依照路由数据中是不是包罗命名空间音讯,分为多个级次举行项目搜索:

  • 率先,固然当前路由数据中留存命名空间音讯,则在缓存中依据控制器名称和命名空间搜索对应的项目,假设找到唯一多个体系,则赶回该类型,找到多少个一向抛非常
  • 其次,借使当前路由数据中不设有命名空间音讯,或在首先等级的搜寻未有找到呼应的门类,并且UseNamespaceFallback==true,此时会获得ControllerBuilder中设置的命名空间音信,利用该消息和控制器名称在缓存中实行项目搜索,借使找到唯一二个项目,则赶回该项目,找到七个一向抛卓殊
  • 谈到底,要是路由数据和ControllerBuilder中都尚无命名空间音讯,或许在以上多少个阶段都并未有检索到对应的Controller类型,那么会忽略命名空间,在缓存中仅依据控制器名称进行项目搜索,如若找到唯一3个档次,则赶回该类型,找到多个平素抛卓殊

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

在缓存中追寻类型的时候,若是是首先次搜索,会调用ControllerTypeCache.EnsureInitialized方法将保存在硬盘中的Xml缓存文件加载到1个字典类型的内部存储器缓存中。尽管该缓存文件不存在,则会遍历当前使用引用的拥有程序集,找出具有public权限的Controller类型(评定规范:达成IController接口、非抽象类、类名以Controller结尾),然后将这么些类型消息进行xml系列化,生成缓存文件保留在硬盘中,以便于下次径直从缓存文件中加载,同时将类型音信分组以字典的情势缓存在内部存款和储蓄器中,进步搜索频率,字典的key为ControllerName(不带命名空间)。

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

威尼斯人线上娱乐 2

获取Controller类型

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

  • 先是,假如当前路由数据中留存命名空间音讯,则在缓存中根据控制器名称和命名空间搜索对应的类型,要是找到唯1一个品种,则赶回该项目,找到多少个一直抛万分
  • 附带,即便当前路由数量中不设有命名空间音信,或在首先等级的寻找未有找到呼应的连串,并且UseNamespaceFallback==true,此时会取得ControllerBuilder中设置的命名空间音讯,利用该音信和控制器名称在缓存中实行项目搜索,要是找到唯12个品类,则赶回该品种,找到四个平素抛万分
  • 终极,如若路由数据和ControllerBuilder中都从未有过命名空间新闻,恐怕在上述多少个阶段都并没有寻找到相应的Controller类型,那么会忽视命名空间,在缓存中仅根据控制器名称实行项目搜索,假如找到唯壹一个门类,则赶回该类型,找到四个向来抛非常

据此,命名空间的先期级是:RouteData > ControllerBuilder

在缓存中搜寻类型的时候,假诺是首先次搜索,会调用ControllerTypeCache.EnsureInitialized方法将保留在硬盘中的Xml缓存文件加载到多少个字典类型的内部存款和储蓄器缓存中。借使该缓存文件不设有,则会遍历当前选取引用的装有程序集,找出富有public权限的Controller类型(判定标准:完毕IController接口、非抽象类、类名以Controller结尾),然后将那些类型新闻进行xml类别化,生成缓存文件保留在硬盘中,以便于下次径直从缓存文件中加载,同时将类型音讯分组以字典的格局缓存在内部存款和储蓄器中,进步搜索频率,字典的key为ControllerName(不带命名空间)。

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

威尼斯人线上娱乐 3

TempData

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

创建Controller对象

获取Controller类型今后,接下去就要拓展Controller对象的创始。在DefaultControllerFactory类的源码中能够见见,同ControllerBuilder类似,该类的构造函数中也实例化了2个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类似,该类的构造函数中也实例化了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相关模块实行扩展的时候,也有三种艺术能够采纳。

  • TempData
    是1个延续于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仅存款和储蓄权且数据,里面包车型大巴数量在首先次读取之后会被移除,即:只可以被读取二回;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仅存储暂且数据,里面包车型地铁数目在率先次读取之后会被移除,即:只可以被读取1次;ViewData和ViewBag保存的是壹模1样份数据,只可是ViewBag是动态目的,对ViewData举办了打包。

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

下边不难说一下TempData的兑现原理。


TempData

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

你可以按使用 ViewDataDictionary 对象的1模壹样方式利用 TempDataDictionary
对象传递数据。 可是,TempDataDictionary
对象中的数据仅从二个伸手保持到下一个伸手,除非您采用 Keep
方法将二个或几个键标记为需保存。
倘使键已标记为需保留,则会为下贰个伸手保留该键。
TempDataDictionary
对象的超人用法是,在数码重定向到1个操作方法时从另1个操作方法传递数据。
例如,操作方法恐怕会在调用 RedirectToAction
方法从前,将关于错误的新闻存款和储蓄在控制器的 TempData 属性(该属性重临TempDataDictionary 对象)中。
然后,下3个操作方法能够处理错误并显现展现错误音讯的视图。

TempData的风味正是能够在四个Action之间传递数据,它会保留1份数据到下三个Action,并乘胜再下1个Action的赶来而失效。所以它被用在五个Action之间来保存数据,比如,那样三个风貌,你的2个Action接受一些post的数码,然后交到另一个Action来拍卖,并出示到页面,那时就足以选取TempData来传递这份数据。

TempData完成了IDictionary接口,同时内部含有2个IDictionary类型的民用字段,并添加了相关方法对字典字段的操作实行了控制,那鲜明是代理方式的2个利用。因为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存放能够被反复造访的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遍呼吁中运用。

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,由此通过该格局读取数据不影响多少的情状,该条数据还能在下1遍呼吁中被接纳。

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
对象中的数据仅从贰个呼吁保持到下贰个伸手,除非您选择 Keep
方法将三个或多个键标记为需保存。
即使键已标记为需保留,则会为下二个伸手保留该键。
TempDataDictionary
对象的天下第2用法是,在数量重定向到3个操作方法时从另3个操作方法传递数据。
例如,操作方法可能会在调用 RedirectToAction
方法在此之前,将关于错误的音信囤积在控制器的 TempData 属性(该属性重回TempDataDictionary 对象)中。
然后,下1个操作方法能够处理错误并展现显示错误消息的视图。

TempData的特点就是足以在多少个Action之间传递数据,它会保留1份数据到下3个Action,并乘胜再下一个Action的赶来而失效。所以它被用在四个Action之间来保存数据,比如,那样贰个场合,你的一个Action接受壹些post的数量,然后交到另三个Action来拍卖,并展现到页面,这时就足以选取TempData来传递那份数据。

TempData达成了IDictionary接口,同时内部含有多个IDictionary类型的个人字段,并添加了相关方法对字典字段的操作实行了控制,那明显是代理形式的3个利用。因为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存放能够被反复拜访的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、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,因而通过该措施读取数据不影响多少的情事,该条数据还可以在下2次呼吁中被利用。

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至下一次访问。

  • 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 中融为一体了以下二种ActionResults类型及相应的帮忙类措施:

  1. ViewResult –
    使用Controller中提供的View()主意再次回到一个ViewResult用来显示钦赐或暗中认可的View。
  2. PartialViewResult-
    使用Controller中提供的PartialView()办法重返2个PartialViewResult用来显现钦点或暗许的分部视图。
  3. RedirectResult –
    使用Controller中提供的Redirect()方法重临一个RedirectResult用来倡导多个HTTP 30一 或 30二 到钦定ULANDL的跳转。
  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用来显现一段JavaScript代码,一般仅用于Ajax请求的处境。
  8. FileResult –
    使用Controller中提供的File()艺术重临贰个FileResult用来显示文件(PDF,
    DOC, Excel等)内容。
  9. EmptyResult – 重返多个空的结果。
  10. HttpNotFoundResult –
    使用Controller中提供的HttpNotFound()格局重返叁个HTTP 40肆动静。
  11. HttpUnauthorizedResult –
    重回2个HttpUnauthorizedResult类型用来代表HTTP
    40一景况(未证实)。用来须求用户登录以成就认证。
  12. HttpStatusCodeResult – 再次来到HttpStatusCodeResult用来代表内定Http状态。

Q5六. ASP.NET MVC中怎么着标记Non-Action方法?
Ans. ASP.NET MVC 将具备的集体方法私下认可为action方法。
万一不想有些公共的不2法门被揭发为Action,仅供给用NonActionAttribute标记方法即可。

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

Q伍7. 能不能够更改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();
}

Q59. 如何决定一个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. 怎样判定2个AJAX请求?
Ans. 通过动用Request.IsAjaxRequest()来判断。

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


相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图