zoukankan      html  css  js  c++  java
  • ASP.NET MVC5学习笔记之Action参数模型绑定值提供体系

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

    一. 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

    b. FormValueProviderFactory 创建FormValueProvider , 提供请求表单的值

     1 public sealed class FormValueProviderFactory : ValueProviderFactory
     2     {
     3         private readonly UnvalidatedRequestValuesAccessor _unvalidatedValuesAccessor;
     4 
     5         public FormValueProviderFactory()
     6             : this(null)
     7         {
     8         }
     9 
    10         // For unit testing
    11         internal FormValueProviderFactory(UnvalidatedRequestValuesAccessor unvalidatedValuesAccessor)
    12         {
    13             _unvalidatedValuesAccessor = unvalidatedValuesAccessor ?? (cc => new UnvalidatedRequestValuesWrapper(cc.HttpContext.Request.Unvalidated));
    14         }
    15 
    16         public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    17         {
    18             if (controllerContext == null)
    19             {
    20                 throw new ArgumentNullException("controllerContext");
    21             }
    22 
    23             return new FormValueProvider(controllerContext, _unvalidatedValuesAccessor(controllerContext));
    24         }
    25     }
    View Code

    c. JsonValueProviderFactory 处理json请求类型(application/json), 创建DictionaryValueProvider,

      1  public sealed class JsonValueProviderFactory : ValueProviderFactory
      2     {
      3         private static void AddToBackingStore(EntryLimitedDictionary backingStore, string prefix, object value)
      4         {
      5             IDictionary<string, object> d = value as IDictionary<string, object>;
      6             if (d != null)
      7             {
      8                 foreach (KeyValuePair<string, object> entry in d)
      9                 {
     10                     AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
     11                 }
     12                 return;
     13             }
     14 
     15             IList l = value as IList;
     16             if (l != null)
     17             {
     18                 for (int i = 0; i < l.Count; i++)
     19                 {
     20                     AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
     21                 }
     22                 return;
     23             }
     24 
     25             // primitive
     26             backingStore.Add(prefix, value);
     27         }
     28 
     29         private static object GetDeserializedObject(ControllerContext controllerContext)
     30         {
     31             if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
     32             {
     33                 // not JSON request
     34                 return null;
     35             }
     36 
     37             StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
     38             string bodyText = reader.ReadToEnd();
     39             if (String.IsNullOrEmpty(bodyText))
     40             {
     41                 // no JSON data
     42                 return null;
     43             }
     44 
     45             JavaScriptSerializer serializer = new JavaScriptSerializer();
     46             object jsonData = serializer.DeserializeObject(bodyText);
     47             return jsonData;
     48         }
     49 
     50         public override IValueProvider GetValueProvider(ControllerContext controllerContext)
     51         {
     52             if (controllerContext == null)
     53             {
     54                 throw new ArgumentNullException("controllerContext");
     55             }
     56 
     57             object jsonData = GetDeserializedObject(controllerContext);
     58             if (jsonData == null)
     59             {
     60                 return null;
     61             }
     62 
     63             Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
     64             EntryLimitedDictionary backingStoreWrapper = new EntryLimitedDictionary(backingStore);
     65             AddToBackingStore(backingStoreWrapper, String.Empty, jsonData);
     66             return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
     67         }
     68 
     69         private static string MakeArrayKey(string prefix, int index)
     70         {
     71             return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
     72         }
     73 
     74         private static string MakePropertyKey(string prefix, string propertyName)
     75         {
     76             return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
     77         }
     78 
     79         private class EntryLimitedDictionary
     80         {
     81             private static int _maximumDepth = GetMaximumDepth();
     82             private readonly IDictionary<string, object> _innerDictionary;
     83             private int _itemCount = 0;
     84 
     85             public EntryLimitedDictionary(IDictionary<string, object> innerDictionary)
     86             {
     87                 _innerDictionary = innerDictionary;
     88             }
     89 
     90             public void Add(string key, object value)
     91             {
     92                 if (++_itemCount > _maximumDepth)
     93                 {
     94                     throw new InvalidOperationException(MvcResources.JsonValueProviderFactory_RequestTooLarge);
     95                 }
     96 
     97                 _innerDictionary.Add(key, value);
     98             }
     99 
    100             private static int GetMaximumDepth()
    101             {
    102                 NameValueCollection appSettings = ConfigurationManager.AppSettings;
    103                 if (appSettings != null)
    104                 {
    105                     string[] valueArray = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers");
    106                     if (valueArray != null && valueArray.Length > 0)
    107                     {
    108                         int result;
    109                         if (Int32.TryParse(valueArray[0], out result))
    110                         {
    111                             return result;
    112                         }
    113                     }
    114                 }
    115 
    116                 return 1000; // Fallback default
    117             }
    118         }
    119     }
    View Code

    d. RouteDataValueProviderFactory 创建RouteDataValueProvider, 提供路由信息相关值

     1  public sealed class RouteDataValueProviderFactory : 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 RouteDataValueProvider(controllerContext);
    11         }
    12     }
    View Code

    e. QueryStringValueProviderFactory 创建QueryStringValueProvider, 提供查询字符串值

     1 public sealed class QueryStringValueProviderFactory : ValueProviderFactory
     2     {
     3         private readonly UnvalidatedRequestValuesAccessor _unvalidatedValuesAccessor;
     4 
     5         public QueryStringValueProviderFactory()
     6             : this(null)
     7         {
     8         }
     9 
    10         // For unit testing
    11         internal QueryStringValueProviderFactory(UnvalidatedRequestValuesAccessor unvalidatedValuesAccessor)
    12         {
    13             _unvalidatedValuesAccessor = unvalidatedValuesAccessor ?? (cc => new UnvalidatedRequestValuesWrapper(cc.HttpContext.Request.Unvalidated));
    14         }
    15 
    16         public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    17         {
    18             if (controllerContext == null)
    19             {
    20                 throw new ArgumentNullException("controllerContext");
    21             }
    22 
    23             return new QueryStringValueProvider(controllerContext, _unvalidatedValuesAccessor(controllerContext));
    24         }
    25     }
    View Code

    f. HttpFileCollectionValueProviderFactory创建HttpFileCollectionValueProvider上传文件值提供

     1 public sealed class HttpFileCollectionValueProviderFactory : 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 HttpFileCollectionValueProvider(controllerContext);
    11         }
    12     }
    View Code

     整体ValueProvider的继承体系统如下图:

  • 相关阅读:
    053403
    053402
    053401
    053400
    053399
    053398
    053397
    053396
    053395
    第k小数
  • 原文地址:https://www.cnblogs.com/jjyjjyjjy/p/3730161.html
Copyright © 2011-2022 走看看