在MVC框架中NameValueCollectionValueProvider采用一个NameValueCollection作为数据源,DictionnaryValueProvider的数据源类型自然就是一个Dictionnary。
NameValueCollection和Dictionnary都是一个键值对的集合,它们之间的不同之处在NameValueCollection运行元素具有相同的Key,Dictionnary却要求元素的Key具有唯一性。
DictionnaryValueProvider
在MVC框架默认的值提供程序中,ChildActionValueProvider,RouteDataValueProvider,HttpFileCollectionValueProvider等值提供程序都继承了DictionaryValueProvider类;
public class DictionaryValueProvider<TValue> : IValueProvider, IEnumerableValueProvider { private PrefixContainer _prefixContainer; private readonly Dictionary<string, ValueProviderResult> _values = new Dictionary<string, ValueProviderResult>(StringComparer.OrdinalIgnoreCase); public DictionaryValueProvider(IDictionary<string, TValue> dictionary, CultureInfo culture) { if (dictionary == null) { throw new ArgumentNullException("dictionary"); } foreach (KeyValuePair<string, TValue> entry in dictionary) { object rawValue = entry.Value; string attemptedValue = Convert.ToString(rawValue, culture); _values[entry.Key] = new ValueProviderResult(rawValue, attemptedValue, culture); } } }
在DictionaryValueProvider的构造函数中接收一个key-value的字典类型,在函数内部逐个遍历这个字典类型,将每一项转换为ValueProviderResult类型,并以key-value的形式存储在Dictionary<string, ValueProviderResult> _values 中,在数据的查询都是在这个value字典里面进行查询;
DictionaryValueProvider类继承了IValueProvider和IEnumerableValueProvider接口;
IValueProvider:声明GetValue方法和ContainsPrefix方法,前者根据key获得对应的Value,这个key有可能是带前缀的;后者是判断是否有给定前缀的key。
IEnumerableValueProvider:继承IValueProvider。针对目标类型为集合(Collection)的数据提供,生命了GetKeysFromPrefix方法,返回容器中具有指定前缀的Key,这个过程默认是需要验证的。
private PrefixContainer PrefixContainer { get { if (_prefixContainer == null) { _prefixContainer = new PrefixContainer(_values.Keys); } return _prefixContainer; } }
public virtual bool ContainsPrefix(string prefix) { return PrefixContainer.ContainsPrefix(prefix); } public virtual ValueProviderResult GetValue(string key) { if (key == null) { throw new ArgumentNullException("key"); } ValueProviderResult valueProviderResult; _values.TryGetValue(key, out valueProviderResult); return valueProviderResult; } public virtual IDictionary<string, string> GetKeysFromPrefix(string prefix) { return PrefixContainer.GetKeysFromPrefix(prefix); }
DictionaryValueProvider类中的ContainsPrefix方法和GetKeysFromPrefix方法实际上是调用的PrefixContainer中的对应方法,在MVC框架中默认为PrefixContainer类;而GetValue根据key查找value字典的数据;因此DictionaryValueProvider类的作用实际上是做数据加工的作用,对应数据进行筛选的数据源来源于外部,通过构造方法的参数传入;
ChildActionValueProvider
public sealed class ChildActionValueProvider : DictionaryValueProvider<object> { private static string _childActionValuesKey = Guid.NewGuid().ToString(); public ChildActionValueProvider(ControllerContext controllerContext) : base(controllerContext.RouteData.Values, CultureInfo.InvariantCulture) { } }
ChildActionValueProvider专门服务于针对子Action方法参数的Model绑定。ChildActionValueProvider类在创建的过程中会把controllerContext.RouteData.Values坐位数据源传入到DictionaryValueProvider类中;但是在ChildActionValueProvider类中重写了DictionaryValueProvider类的GetValue方法;
private static string _childActionValuesKey = Guid.NewGuid().ToString(); public override ValueProviderResult GetValue(string key) { if (key == null) { throw new ArgumentNullException("key"); } ValueProviderResult explicitValues = base.GetValue(ChildActionValuesKey); if (explicitValues != null) {
DictionaryValueProvider<object> rawExplicitValues = explicitValues.RawValue as DictionaryValueProvider<object>; if (rawExplicitValues != null) { return rawExplicitValues.GetValue(key); } } return null; }
当调用ChildActionValueProvider的GetValue方法获取指定Key的值时,实际上并不会直接根据指定的Key去获取对应的值,而是根据通过其静态字段_childActionValuesKey值去获取对应的DictionaryValueProvider<object>对象。然后再调用该对象的GetValue根据指定的Key去获得相应的值。
RouteDataValueProvider
public sealed class RouteDataValueProvider : DictionaryValueProvider<object> { public RouteDataValueProvider(ControllerContext controllerContext) : base(controllerContext.RouteData.Values, CultureInfo.InvariantCulture) { } }
RouteDataValueProvider类是将路由数据(controllerContext.RouteData.Values)做为数据源;
HttpFileCollectionValueProvider
public sealed class HttpFileCollectionValueProvider : DictionaryValueProvider<HttpPostedFileBase[]> { private static readonly Dictionary<string, HttpPostedFileBase[]> _emptyDictionary = new Dictionary<string, HttpPostedFileBase[]>(); public HttpFileCollectionValueProvider(ControllerContext controllerContext) : base(GetHttpPostedFileDictionary(controllerContext), CultureInfo.InvariantCulture) { } private static Dictionary<string, HttpPostedFileBase[]> GetHttpPostedFileDictionary(ControllerContext controllerContext) { HttpFileCollectionBase files = controllerContext.HttpContext.Request.Files; // fast-track common case of no files if (files.Count == 0) { return _emptyDictionary; } List<KeyValuePair<string, HttpPostedFileBase>> mapping = new List<KeyValuePair<string, HttpPostedFileBase>>(); string[] allKeys = files.AllKeys; for (int i = 0; i < files.Count; i++) { string key = allKeys[i]; if (key != null) { HttpPostedFileBase file = HttpPostedFileBaseModelBinder.ChooseFileOrNull(files[i]); mapping.Add(new KeyValuePair<string, HttpPostedFileBase>(key, file)); } } var grouped = mapping.GroupBy(el => el.Key, el => el.Value, StringComparer.OrdinalIgnoreCase); return grouped.ToDictionary(g => g.Key, g => g.ToArray(), StringComparer.OrdinalIgnoreCase); }
HttpFileCollectionValueProvider类是文件上传的的值提供程序,在HTTP请求的HttpRequestBase对象中,上传文件通过只读属性Files表示;在构造函数中通过GetHttpPostedFileDictionary方法创建一个key为文件名Value为HttpPostedFileBase类型的字典类型作为数据源;