这一节我们关注模型绑定的值提供体系,先来介绍几个重要的接口

一. IValueProvider,接口定义如下:

1 public interface IValueProvider
2     {
3         
4         bool ContainsPrefix(string prefix);
5        
6         ValueProviderResult GetValue(string key);
7     }

从上面可以看出,IValueProvider定义了两个方法, 一个是检测是否包含指定的前缀,一个是通过指定的Key获取查询结果.这里前缀的概念主要是针对复杂类型的绑定,复杂类型包含属性,而属性的类型又是一个复杂类型,这样一层层下来,当我们在绑定类型的属性时,我们必须有一种机制确定该属性的值是从属于某个对象的,这就有了前缀的概念。系统定义了以下几种类型的绑定语法:

1.简单类型

  prefix == 变量的名称

2. 复杂类型

  prefix 变量名称

  prefix.Name

  prefix.Address.Name

3. 数组

  a. 同名数据项

    多个同名数据项, ValueProviderResult直接转换成数组

  b. 基于索引的数组绑定

    [0].Name

    [0].PhoneNo

    [0].Email

    [1].Name

    [1].PhoneNo

    [1].Email

4,集合IEnumerable<T> 与数组类似

5. 字典

  [0].Key

  [0].Value.Name

  [0].Value.EmailAddress

  [1].Key

  [2].Value.Name

  [3].Value.EmailAddress

 二. ValueProviderResult类型

 1  [Serializable]
 2     public class ValueProviderResult
 3     {
 4         protected ValueProviderResult();
 5        
 6         public ValueProviderResult(object rawValue, string attemptedValue, CultureInfo culture);
 7 
 8         public string AttemptedValue { get; protected set; }
 9        
10         public CultureInfo Culture { get; protected set; }
11        
12         public object RawValue { get; protected set; }
13 
14         public object ConvertTo(Type type);
15        
16         public virtual object ConvertTo(Type type, CultureInfo culture);
17     }

   AttemptedValue表示从值的字符串表示,RawValue 表示值的原始值. 同时看到定义类型转换接口。 这里转换的代码值得研究一下:

 1 public virtual object ConvertTo(Type type, CultureInfo culture)
 2         {
 3             if (type == null)
 4             {
 5                 throw new ArgumentNullException("type");
 6             }
 7 
 8             CultureInfo cultureToUse = culture ?? Culture;
 9             return UnwrapPossibleArrayType(cultureToUse, RawValue, type);
10         }
11 
12         private static object UnwrapPossibleArrayType(CultureInfo culture, object value, Type destinationType)
13         {
14             if (value == null || destinationType.IsInstanceOfType(value))
15             {
16                 return value;
17             }
18 
19             // array conversion results in four cases, as below
20             Array valueAsArray = value as Array;
21             if (destinationType.IsArray)
22             {
23                 Type destinationElementType = destinationType.GetElementType();
24                 if (valueAsArray != null)
25                 {
26                     // case 1: both destination + source type are arrays, so convert each element
27                     IList converted = Array.CreateInstance(destinationElementType, valueAsArray.Length);
28                     for (int i = 0; i < valueAsArray.Length; i++)
29                     {
30                         converted[i] = ConvertSimpleType(culture, valueAsArray.GetValue(i), destinationElementType);
31                     }
32                     return converted;
33                 }
34                 else
35                 {
36                     // case 2: destination type is array but source is single element, so wrap element in array + convert
37                     object element = ConvertSimpleType(culture, value, destinationElementType);
38                     IList converted = Array.CreateInstance(destinationElementType, 1);
39                     converted[0] = element;
40                     return converted;
41                 }
42             }
43             else if (valueAsArray != null)
44             {
45                 // case 3: destination type is single element but source is array, so extract first element + convert
46                 if (valueAsArray.Length > 0)
47                 {
48                     value = valueAsArray.GetValue(0);
49                     return ConvertSimpleType(culture, value, destinationType);
50                 }
51                 else
52                 {
53                     // case 3(a): source is empty array, so can't perform conversion
54                     return null;
55                 }
56             }
57             // case 4: both destination + source type are single elements, so convert
58             return ConvertSimpleType(culture, value, destinationType);
59         }

 1. 如果值是目标类型的实例,直接返回

 2. 尝试转换为数组,这里列了4种情况。

 3. 单一类型转换

 再来看一下ConvertSimpleType的代码:

 1 private static object ConvertSimpleType(CultureInfo culture, object value, Type destinationType)
 2         {
 3             if (value == null || destinationType.IsInstanceOfType(value))
 4             {
 5                 return value;
 6             }
 7 
 8             // if this is a user-input value but the user didn't type anything, return no value
 9             string valueAsString = value as string;
10             if (valueAsString != null && String.IsNullOrWhiteSpace(valueAsString))
11             {
12                 return null;
13             }
14 
15             // In case of a Nullable object, we extract the underlying type and try to convert it.
16             Type underlyingType = Nullable.GetUnderlyingType(destinationType);
17 
18             if (underlyingType != null)
19             {
20                 destinationType = underlyingType;
21             }
22 
23             // String doesn't provide convertibles to interesting types, and thus it will typically throw rather than succeed.
24             if (valueAsString == null)
25             {
26                 // If the source type implements IConvertible, try that first
27                 IConvertible convertible = value as IConvertible;
28                 if (convertible != null)
29                 {
30                     try
31                     {
32                         return convertible.ToType(destinationType, culture);
33                     }
34                     catch
35                     {
36                     }
37                 }
38             }
39 
40             // Last resort, look for a type converter
41             TypeConverter converter = TypeDescriptor.GetConverter(destinationType);
42             bool canConvertFrom = converter.CanConvertFrom(value.GetType());
43             if (!canConvertFrom)
44             {
45                 converter = TypeDescriptor.GetConverter(value.GetType());
46             }
47             if (!(canConvertFrom || converter.CanConvertTo(destinationType)))
48             {
49                 // EnumConverter cannot convert integer, so we verify manually
50                 if (destinationType.IsEnum && value is int)
51                 {
52                     return Enum.ToObject(destinationType, (int)value);
53                 }
54 
55                 string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ValueProviderResult_NoConverterExists,
56                                                value.GetType().FullName, destinationType.FullName);
57                 throw new InvalidOperationException(message);
58             }
59 
60             try
61             {
62                 object convertedValue = (canConvertFrom)
63                                             ? converter.ConvertFrom(null /* context */, culture, value)
64                                             : converter.ConvertTo(null /* context */, culture, value, destinationType);
65                 return convertedValue;
66             }
67             catch (Exception ex)
68             {
69                 string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ValueProviderResult_ConversionThrew,
70                                                value.GetType().FullName, destinationType.FullName);
71                 throw new InvalidOperationException(message, ex);
72             }
73         }

  这里也考虑了几种情况转换
1. 值是目标类型的实例直接返回
2. 值是空串返回null
3. 可空类型取其下的真正类型
4. 尝试利用IConvertible转换
5. 利用目标类型和值类型的TypeConverter
6. 检查目标类型是Enum和值类型是否int

 三. ValueProviderFactory 

 public abstract class ValueProviderFactory
    {
        public abstract IValueProvider GetValueProvider(ControllerContext controllerContext);
    }

表示的值提供对象的创健工厂.

四. ValueProvider创建工厂和具体ValueProvider介绍

 ValueProvider的调用入口是Controller.ValueProvider属性,它是调用ValueProviderFactories.Factories.GetValueProvider()返回值,看看ValueProviderFactories的定义

 1 public static class ValueProviderFactories
 2     {
 3         private static readonly ValueProviderFactoryCollection _factories = new ValueProviderFactoryCollection()
 4         {
 5             new ChildActionValueProviderFactory(),
 6             new FormValueProviderFactory(),
 7             new JsonValueProviderFactory(),
 8             new RouteDataValueProviderFactory(),
 9             new QueryStringValueProviderFactory(),
10             new HttpFileCollectionValueProviderFactory(),
11         };
12 
13         public static ValueProviderFactoryCollection Factories
14         {
15             get { return _factories; }
16         }
17     }

可以了解到系统内置几种ValueProviderFactory, 下面依次来了解.

a. ChildActionValueProviderFactory 创建ChildActionValueProvider, 提供在HtmlHelper.Action方法附加的路由信息

 1 public sealed class ChildActionValueProviderFactory : ValueProviderFactory
 2     {
 3         public override IValueProvider GetValueProvider(ControllerContext controllerContext)
 4         {
 5             if (controllerContext == null)
 6             {
 7                 throw new ArgumentNullException("controllerContext");
 8             }
 9 
10             return new ChildActionValueProvider(controllerContext);
11         }
12     }
View Code

相关文章:

  • 2022-12-23
  • 2021-12-31
  • 2022-12-23
  • 2021-07-14
  • 2021-10-20
  • 2021-05-21
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-12-26
  • 2021-07-23
  • 2022-01-08
相关资源
相似解决方案