Code Copied

ASP.NET MVC分析——Controller的TempData属性

TempData、ViewData和ViewBag是ASP.NET MVC中常用的3种数据容器。

TempData:TempData是一个键值对数据容器,但在TempData种设置的变量在第一次被读取后会被移除。

ControllerBase中是包含TempData、ViewData和ViewBag这3个属性的。

ControllerBase的TempData属性

private TempDataDictionary _tempDataDictionary;
public TempDataDictionary TempData
{
    get
    {
        if (ControllerContext != null && ControllerContext.IsChildAction)
        {
            return ControllerContext.ParentActionViewContext.TempData;
        }
        if (_tempDataDictionary == null)
        {
            _tempDataDictionary = new TempDataDictionary();
        }
        return _tempDataDictionary;
    }
    set { _tempDataDictionary = value; }
}

TempData属性的类型是TempDataDictionary,以下是TempDataDictionary的部分源码

public class TempDataDictionary : IDictionary<string, object>
{
    private Dictionary<string, object> _data;
    private HashSet<string> _initialKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

    public TempDataDictionary()
    {
        _data = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
    }

    public object this[string key]
    {
        get
        {
            object value;
            if (TryGetValue(key, out value))
            {
                _initialKeys.Remove(key);
                return value;
            }
            return null;
        }
        set
        {
            _data[key] = value;
            _initialKeys.Add(key);
        }
    }

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

    public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
    {
        IDictionary<string, object> providerDictionary = tempDataProvider.LoadTempData(controllerContext);
        _data = (providerDictionary != null)
            ? new Dictionary<string, object>(providerDictionary, StringComparer.OrdinalIgnoreCase)
            : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        _initialKeys = new HashSet<string>(_data.Keys, StringComparer.OrdinalIgnoreCase);
        _retainedKeys.Clear();
    }
}

黄色区域的代码很明显地展示了TempDataDictionary的特点:当TempDataDictionary的某一个Key被读取后,该Key会被移除

TempData的取值和赋值过程

TempDataDictionary的数据存储在私有字段Dictionary<string, object> _data中,字段_data在Load()方法中进行赋值。

void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider)

Load方法包含两个参数:ControllerContext和ITempDataProvider。
在Load()方法中,ControllerContext作为ITempDataProvider的LoadTempData()方法的参数取到_data字段的数据源。

在ASP.NET MVC中,ITempDataProvider只有一个实现SessionStateTempDataProvider:

public class SessionStateTempDataProvider : ITempDataProvider
{
    internal const string TempDataSessionStateKey = "__ControllerTempData";

    public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
    {
        HttpSessionStateBase session = controllerContext.HttpContext.Session;

        if (session != null)
        {
            Dictionary<string, object> tempDataDictionary = session[TempDataSessionStateKey] as Dictionary<string, object>;

            if (tempDataDictionary != null)
            {
                // If we got it from Session, remove it so that no other request gets it
                session.Remove(TempDataSessionStateKey);
                return tempDataDictionary;
            }
        }

        return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
    }

    public virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }

        HttpSessionStateBase session = controllerContext.HttpContext.Session;
        bool isDirty = (values != null && values.Count > 0);

        if (session == null)
        {
            if (isDirty)
            {
                throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled);
            }
        }
        else
        {
            if (isDirty)
            {
                session[TempDataSessionStateKey] = values;
            }
            else
            {
                // Since the default implementation of Remove() (from SessionStateItemCollection) dirties the
                // collection, we shouldn't call it unless we really do need to remove the existing key.
                if (session[TempDataSessionStateKey] != null)
                {
                    session.Remove(TempDataSessionStateKey);
                }
            }
        }
    }
}
}

SessionStateTempDataProvider类包含两个方法:

  1. LoadTempData()方法:从Session中获取Key为”_ControllerTempData”的Value,取完值后将Session[”_ControllerTempData”]移除
  2. SaveTempData()方法:把数据保存到Session[”_ControllerTempData”]中。

所以,本质上TempDataDictionary的数据的赋值和获取是借助Session来实现的,当Session[”_ControllerTempData”]被读取后,该Session被立即删除