.Net Core—-关于MVC中TempData持久化问题

最近在做mvc跨控制器传值的时候发现一个问题,就是有时候TempData的值为null,然后查阅了许多资料,发现了许多都是逻辑和原理什么的(想看原理可以查看原理的文章,本文是用法),但是真正解决的办法什么案例都没有,

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

于是就把自己的代码当成案例给贴出来,方便更直观的解决问题。

在UrlRoutingModule模块中,将请求处理程序映射到了MvcHandler中,因此,说起Controller的激活,首先要从MvcHandler入手,MvcHandler实现了三个接口:IHttpAsyncHandler,
IHttpHandler, IRequiresSessionState。
其处理逻辑主要实现在同步和异步的ProcessRequest方法中,总的来说,该方法在执行的时候,大致经历以下几个步骤:

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

  1. 预处理(在响应头中添加版本信息并去除未赋值的可选路由参数)
  2. 通过ControllerBuilder获取ControlerFactory,并使用Controller工厂创建Controller
  3. 根据是否是异步处理,调用Controller中相应的方法(ExecuteCore或BeginExecute)
  4. 释放Controller
        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是如何创建出来的。

在当前Action方法中调用Keep方法则保证在当前请求中TempData对象中所存储的键都不会被移除。

Controller的创建是通过ControllerFactory实现的,而ControllerFactory的创建又是在ControllerBuilder中完成的,因此我们先了解一下ControllerBuilder的工作原理。

 

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类中才能实现真正的注册,我们知道,如果是单纯的引用赋值,那么更改一个引用并不会对另外一个引用造成改变,比如:

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等,实现了对多种对象的创建。

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图