ASP.NET MVC5学习笔记之Action参数模型绑定值提供体系
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了ASP.NET MVC5学习笔记之Action参数模型绑定值提供体系,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含13188字,纯文字阅读大概需要19分钟。
内容图文
这一节我们关注模型绑定的值提供体系,先来介绍几个重要的接口
一. IValueProvider,接口定义如下:
1 public interface IValueProvider 2 { 3 4 bool ContainsPrefix(string prefix); 56 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 8publicstring AttemptedValue { get; protectedset; } 910public CultureInfo Culture { get; protectedset; } 1112publicobject RawValue { get; protectedset; } 1314publicobject ConvertTo(Type type); 1516publicvirtualobject ConvertTo(Type type, CultureInfo culture); 17 }
AttemptedValue表示从值的字符串表示,RawValue 表示值的原始值. 同时看到定义类型转换接口。 这里转换的代码值得研究一下:
1 public virtual object ConvertTo(Type type, CultureInfo culture) 2 { 3 if (type == null) 4 { 5thrownew ArgumentNullException("type"); 6 } 7 8 CultureInfo cultureToUse = culture ?? Culture; 9return UnwrapPossibleArrayType(cultureToUse, RawValue, type); 10 } 1112privatestaticobject UnwrapPossibleArrayType(CultureInfo culture, object value, Type destinationType) 13 { 14if (value == null || destinationType.IsInstanceOfType(value)) 15 { 16return value; 17 } 1819// array conversion results in four cases, as below20 Array valueAsArray = value as Array; 21if (destinationType.IsArray) 22 { 23 Type destinationElementType = destinationType.GetElementType(); 24if (valueAsArray != null) 25 { 26// case 1: both destination + source type are arrays, so convert each element27 IList converted = Array.CreateInstance(destinationElementType, valueAsArray.Length); 28for (int i = 0; i < valueAsArray.Length; i++) 29 { 30 converted[i] = ConvertSimpleType(culture, valueAsArray.GetValue(i), destinationElementType); 31 } 32return converted; 33 } 34else35 { 36// case 2: destination type is array but source is single element, so wrap element in array + convert37object element = ConvertSimpleType(culture, value, destinationElementType); 38 IList converted = Array.CreateInstance(destinationElementType, 1); 39 converted[0] = element; 40return converted; 41 } 42 } 43elseif (valueAsArray != null) 44 { 45// case 3: destination type is single element but source is array, so extract first element + convert46if (valueAsArray.Length > 0) 47 { 48 value = valueAsArray.GetValue(0); 49return ConvertSimpleType(culture, value, destinationType); 50 } 51else52 { 53// case 3(a): source is empty array, so can‘t perform conversion54returnnull; 55 } 56 } 57// case 4: both destination + source type are single elements, so convert58return ConvertSimpleType(culture, value, destinationType); 59 }
1. 如果值是目标类型的实例,直接返回
2. 尝试转换为数组,这里列了4种情况。
3. 单一类型转换
再来看一下ConvertSimpleType的代码:
1 private static object ConvertSimpleType(CultureInfo culture, object value, Type destinationType) 2 { 3if (value == null || destinationType.IsInstanceOfType(value)) 4 { 5return value; 6 } 7 8// if this is a user-input value but the user didn‘t type anything, return no value 9string valueAsString = value asstring; 10if (valueAsString != null && String.IsNullOrWhiteSpace(valueAsString)) 11 { 12returnnull; 13 } 1415// In case of a Nullable object, we extract the underlying type and try to convert it.16 Type underlyingType = Nullable.GetUnderlyingType(destinationType); 1718if (underlyingType != null) 19 { 20 destinationType = underlyingType; 21 } 2223// String doesn‘t provide convertibles to interesting types, and thus it will typically throw rather than succeed.24if (valueAsString == null) 25 { 26// If the source type implements IConvertible, try that first27 IConvertible convertible = value as IConvertible; 28if (convertible != null) 29 { 30try31 { 32return convertible.ToType(destinationType, culture); 33 } 34catch35 { 36 } 37 } 38 } 3940// Last resort, look for a type converter41 TypeConverter converter = TypeDescriptor.GetConverter(destinationType); 42bool canConvertFrom = converter.CanConvertFrom(value.GetType()); 43if (!canConvertFrom) 44 { 45 converter = TypeDescriptor.GetConverter(value.GetType()); 46 } 47if (!(canConvertFrom || converter.CanConvertTo(destinationType))) 48 { 49// EnumConverter cannot convert integer, so we verify manually50if (destinationType.IsEnum && value isint) 51 { 52return Enum.ToObject(destinationType, (int)value); 53 } 5455string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ValueProviderResult_NoConverterExists, 56 value.GetType().FullName, destinationType.FullName); 57thrownew InvalidOperationException(message); 58 } 5960try61 { 62object convertedValue = (canConvertFrom) 63 ? converter.ConvertFrom(null/* context */, culture, value) 64 : converter.ConvertTo(null/* context */, culture, value, destinationType); 65return convertedValue; 66 } 67catch (Exception ex) 68 { 69string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ValueProviderResult_ConversionThrew, 70 value.GetType().FullName, destinationType.FullName); 71thrownew 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 { 5new ChildActionValueProviderFactory(), 6new FormValueProviderFactory(), 7new JsonValueProviderFactory(), 8new RouteDataValueProviderFactory(), 9new QueryStringValueProviderFactory(), 10new HttpFileCollectionValueProviderFactory(), 11 }; 1213publicstatic ValueProviderFactoryCollection Factories 14 { 15get { 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 { 7thrownew ArgumentNullException("controllerContext"); 8 } 910returnnew ChildActionValueProvider(controllerContext); 11 } 12 }
b. FormValueProviderFactory 创建FormValueProvider , 提供请求表单的值
1 public sealed class FormValueProviderFactory : ValueProviderFactory 2 { 3 private readonly UnvalidatedRequestValuesAccessor _unvalidatedValuesAccessor; 4 5 public FormValueProviderFactory() 6 : this(null) 7 { 8 } 910// For unit testing11internal FormValueProviderFactory(UnvalidatedRequestValuesAccessor unvalidatedValuesAccessor) 12 { 13 _unvalidatedValuesAccessor = unvalidatedValuesAccessor ?? (cc => new UnvalidatedRequestValuesWrapper(cc.HttpContext.Request.Unvalidated)); 14 } 1516publicoverride IValueProvider GetValueProvider(ControllerContext controllerContext) 17 { 18if (controllerContext == null) 19 { 20thrownew ArgumentNullException("controllerContext"); 21 } 2223returnnew FormValueProvider(controllerContext, _unvalidatedValuesAccessor(controllerContext)); 24 } 25 }
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>; 6if (d != null) 7 { 8foreach (KeyValuePair<string, object> entry in d) 9 { 10 AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value); 11 } 12return; 13 } 14 15 IList l = value as IList; 16if (l != null) 17 { 18for (int i = 0; i < l.Count; i++) 19 { 20 AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]); 21 } 22return; 23 } 24 25// primitive 26 backingStore.Add(prefix, value); 27 } 28 29privatestaticobject GetDeserializedObject(ControllerContext controllerContext) 30 { 31if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) 32 { 33// not JSON request 34returnnull; 35 } 36 37 StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream); 38string bodyText = reader.ReadToEnd(); 39if (String.IsNullOrEmpty(bodyText)) 40 { 41// no JSON data 42returnnull; 43 } 44 45 JavaScriptSerializer serializer = new JavaScriptSerializer(); 46object jsonData = serializer.DeserializeObject(bodyText); 47return jsonData; 48 } 49 50publicoverride IValueProvider GetValueProvider(ControllerContext controllerContext) 51 { 52if (controllerContext == null) 53 { 54thrownew ArgumentNullException("controllerContext"); 55 } 56 57object jsonData = GetDeserializedObject(controllerContext); 58if (jsonData == null) 59 { 60returnnull; 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); 66returnnew DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture); 67 } 68 69privatestaticstring MakeArrayKey(string prefix, int index) 70 { 71return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]"; 72 } 73 74privatestaticstring MakePropertyKey(string prefix, string propertyName) 75 { 76return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName; 77 } 78 79privateclass EntryLimitedDictionary 80 { 81privatestaticint _maximumDepth = GetMaximumDepth(); 82privatereadonly IDictionary<string, object> _innerDictionary; 83privateint _itemCount = 0; 84 85public EntryLimitedDictionary(IDictionary<string, object> innerDictionary) 86 { 87 _innerDictionary = innerDictionary; 88 } 89 90publicvoid Add(string key, object value) 91 { 92if (++_itemCount > _maximumDepth) 93 { 94thrownew InvalidOperationException(MvcResources.JsonValueProviderFactory_RequestTooLarge); 95 } 96 97 _innerDictionary.Add(key, value); 98 } 99100privatestaticint GetMaximumDepth() 101 { 102 NameValueCollection appSettings = ConfigurationManager.AppSettings; 103if (appSettings != null) 104 { 105string[] valueArray = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers"); 106if (valueArray != null && valueArray.Length > 0) 107 { 108int result; 109if (Int32.TryParse(valueArray[0], out result)) 110 { 111return result; 112 } 113 } 114 } 115116return1000; // Fallback default117 } 118 } 119 }
d. RouteDataValueProviderFactory 创建RouteDataValueProvider, 提供路由信息相关值
1 public sealed class RouteDataValueProviderFactory : ValueProviderFactory 2 { 3 public override IValueProvider GetValueProvider(ControllerContext controllerContext) 4 { 5 if (controllerContext == null) 6 { 7thrownew ArgumentNullException("controllerContext"); 8 } 910returnnew RouteDataValueProvider(controllerContext); 11 } 12 }
e. QueryStringValueProviderFactory 创建QueryStringValueProvider, 提供查询字符串值
1 public sealed class QueryStringValueProviderFactory : ValueProviderFactory 2 { 3 private readonly UnvalidatedRequestValuesAccessor _unvalidatedValuesAccessor; 4 5 public QueryStringValueProviderFactory() 6 : this(null) 7 { 8 } 910// For unit testing11internal QueryStringValueProviderFactory(UnvalidatedRequestValuesAccessor unvalidatedValuesAccessor) 12 { 13 _unvalidatedValuesAccessor = unvalidatedValuesAccessor ?? (cc => new UnvalidatedRequestValuesWrapper(cc.HttpContext.Request.Unvalidated)); 14 } 1516publicoverride IValueProvider GetValueProvider(ControllerContext controllerContext) 17 { 18if (controllerContext == null) 19 { 20thrownew ArgumentNullException("controllerContext"); 21 } 2223returnnew QueryStringValueProvider(controllerContext, _unvalidatedValuesAccessor(controllerContext)); 24 } 25 }
f. HttpFileCollectionValueProviderFactory创建HttpFileCollectionValueProvider上传文件值提供
1 public sealed class HttpFileCollectionValueProviderFactory : ValueProviderFactory 2 { 3 public override IValueProvider GetValueProvider(ControllerContext controllerContext) 4 { 5 if (controllerContext == null) 6 { 7thrownew ArgumentNullException("controllerContext"); 8 } 910returnnew HttpFileCollectionValueProvider(controllerContext); 11 } 12 }
整体ValueProvider的继承体系统如下图:
原文:http://www.cnblogs.com/jjyjjyjjy/p/3730161.html
内容总结
以上是互联网集市为您收集整理的ASP.NET MVC5学习笔记之Action参数模型绑定值提供体系全部内容,希望文章能够帮你解决ASP.NET MVC5学习笔记之Action参数模型绑定值提供体系所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。