在MVC框架中action方法中的Model数据的绑定的来源有很多个,可能是http请求中的get参数或是post提交的表单数据,会是json字符串或是路径中的相关数据;MVC框架中针对这些不同的数据来源抽象了IValueProvider接口;
public interface IValueProvider { bool ContainsPrefix(string prefix); ValueProviderResult GetValue(string key); }
IValueProvider接口中的ContainsPrefix方法返回是否包含指定的前缀,GetValue方法时根据指定的key来获取相应的值数据结果;
NameValueCollection
NameValueCollection类时key和value都是字符串的字典,与Dictionary类型不同的是,一个key是可以对应多个值;
NameValueCollection collection = new NameValueCollection(); collection.Add("a", "aa"); collection.Add("a", "bb"); collection.Add("a", "cc"); collection.Add("b", "aa"); string[] rawValue = collection.GetValues("a"); string attemptedValue = collection["a"]; Console.WriteLine(attemptedValue); // aa,bb,cc Console.WriteLine(rawValue); // [aa,bb,cc]
GetValues方法返回自定key的value的数组,attemptedValue 为指定key的value的字符串表示(项之间用,链接);
ValueProviderResult
ValueProviderResult类是存储Model数据的数据来源的值信息;msdn是这样描述的”表示将一个值(如窗体发布或查询字符串中的值)绑定到操作方法参数属性或绑定到该参数本身的结果“
名称 | 说明 | |
---|---|---|
AttemptedValue | 获取或设置要转换为字符串,以便显示的原始值。 | |
Culture | 获取或设置区域性。 | |
RawValue | 获取或设置值提供程序所提供的原始值。 |
在ValueProviderResult 类中含有一个ConvertTo(Type)方法,这个方法的目的是结果封装的值转换为指定的类型。在方法内部中将ValueProviderResult 类的RawValue转化为type类型的值数据。
在ConvertTo(Type)方法内部实际上是调用的UnwrapPossibleArrayType 方法中的object value参数为RawValue,具体的处理逻辑如下:
1.当value值为空或是当前对象value是当前类的实例(当前类可以是value 的类、父类、接口),直接返回value值;
2.当Type的类型是数组类型时,首先会根据destinationType.GetElementType来获取数组元素的类型
2.1 当value值能够转化为Array 数组时,然后根据转换Array 数组的长度和数组元素的类型通过Array.CreateInstance方法创建一个类型数组,最后逐个遍历转化数组的元素调用内部的ConvertSimpleType方法转换;
2.2当value值不能够转化为Array数组时,type为数组类型时,首先调用内部ConvertSimpleType的方法获取到转换的值,然后创建一个长度为1的数组,将转换后的值写入到数组中。
3.当Type类型不是数组类型时,并且Value的类型为数组类型时
3.1当Value的类型为数组长度大于0时,获取到数组中的第一个元素值,调用内部ConvertSimpleType的方法获取到转换的值
3.2当Value的类型为数组长度为0时,直接返回null值;
4.当以上情况都不满足时,直接调用内部ConvertSimpleType的方法获取到转换的值
private static object UnwrapPossibleArrayType(CultureInfo culture, object value, Type destinationType) { if (value == null || destinationType.IsInstanceOfType(value)) { return value; } Array valueAsArray = value as Array; if (destinationType.IsArray) { Type destinationElementType = destinationType.GetElementType(); if (valueAsArray != null) { IList converted = Array.CreateInstance(destinationElementType, valueAsArray.Length); for (int i = 0; i < valueAsArray.Length; i++) { converted[i] = ConvertSimpleType(culture, valueAsArray.GetValue(i), destinationElementType); } return converted; } else { object element = ConvertSimpleType(culture, value, destinationElementType); IList converted = Array.CreateInstance(destinationElementType, 1); converted[0] = element; return converted; } } else if (valueAsArray != null) { if (valueAsArray.Length > 0) { value = valueAsArray.GetValue(0); return ConvertSimpleType(culture, value, destinationType); } else { return null; } } return ConvertSimpleType(culture, value, destinationType); }
在UnwrapPossibleArrayType 方法中经常会调用ConvertSimpleType方法,从字面意思上理解这个方法是进行简单类型的数组转换;
1.当value值为空或是当前对象value是当前类的实例(当前类可以是value 的类、父类、接口),直接返回value值;
2.当value值转换为字符串后为空字符串时直接返回null
3.通过Nullable.GetUnderlyingType方法获取type是否是可空的值类型,如果是可空的值类型,则返回基础的值类型,当value值不是字符串类型时,value是否继承了IConvertible 接口,如果是,直接调用IConvertible 接口的ToType方法.
4.当以上情况都不满足时,就会通过 TypeDescriptor.GetConverter类获取参数Type的类型转换器,获取到转换器后调用CanConvertFrom方法来获取value的type是否支持这个类型转换器,当不支持转换后,会获取value值的type类型的类型转换器;
4.1如果不支持转换并且类型转换器不能转换到type,这时候就会throw 一个InvalidOperationException异常,不过一种情况除外,当类是枚举类型时,由于EnumConverter不能转换整数,所以我们手动转化,因此会调用Enum.ToObject方法;
4.2如果类型转换器支持转换的话,就直接调用转换器的ConvertFrom的方法,否则调用ConvertTo方法来获取转化的值
private static object ConvertSimpleType(CultureInfo culture, object value, Type destinationType) { if (value == null || destinationType.IsInstanceOfType(value)) { return value; } string valueAsString = value as string; if (valueAsString != null && String.IsNullOrWhiteSpace(valueAsString)) { return null; }
Type underlyingType = Nullable.GetUnderlyingType(destinationType); if (underlyingType != null) { destinationType = underlyingType; } if (valueAsString == null) { IConvertible convertible = value as IConvertible; if (convertible != null) { try { return convertible.ToType(destinationType, culture); } catch { } } } TypeConverter converter = TypeDescriptor.GetConverter(destinationType); bool canConvertFrom = converter.CanConvertFrom(value.GetType()); if (!canConvertFrom) { converter = TypeDescriptor.GetConverter(value.GetType()); } if (!(canConvertFrom || converter.CanConvertTo(destinationType))) { if (destinationType.IsEnum && value is int) { return Enum.ToObject(destinationType, (int)value); } string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ValueProviderResult_NoConverterExists, value.GetType().FullName, destinationType.FullName); throw new InvalidOperationException(message); } try { object convertedValue = (canConvertFrom) ? converter.ConvertFrom(null /* context */, culture, value) : converter.ConvertTo(null /* context */, culture, value, destinationType); return convertedValue; } catch (Exception ex) { string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ValueProviderResult_ConversionThrew, value.GetType().FullName, destinationType.FullName); throw new InvalidOperationException(message, ex); } }