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

一. IValueProvider,接口定义如下:

 public interface IValueProvider
{ bool ContainsPrefix(string prefix); ValueProviderResult GetValue(string key);
}

从上面可以看出,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类型

  [Serializable]
public class ValueProviderResult
{
protected ValueProviderResult(); public ValueProviderResult(object rawValue, string attemptedValue, CultureInfo culture); public string AttemptedValue { get; protected set; } public CultureInfo Culture { get; protected set; } public object RawValue { get; protected set; } public object ConvertTo(Type type); public virtual object ConvertTo(Type type, CultureInfo culture);
}

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

 public virtual object ConvertTo(Type type, CultureInfo culture)
{
if (type == null)
{
throw new ArgumentNullException("type");
} CultureInfo cultureToUse = culture ?? Culture;
return UnwrapPossibleArrayType(cultureToUse, RawValue, type);
} private static object UnwrapPossibleArrayType(CultureInfo culture, object value, Type destinationType)
{
if (value == null || destinationType.IsInstanceOfType(value))
{
return value;
} // array conversion results in four cases, as below
Array valueAsArray = value as Array;
if (destinationType.IsArray)
{
Type destinationElementType = destinationType.GetElementType();
if (valueAsArray != null)
{
// case 1: both destination + source type are arrays, so convert each element
IList converted = Array.CreateInstance(destinationElementType, valueAsArray.Length);
for (int i = ; i < valueAsArray.Length; i++)
{
converted[i] = ConvertSimpleType(culture, valueAsArray.GetValue(i), destinationElementType);
}
return converted;
}
else
{
// case 2: destination type is array but source is single element, so wrap element in array + convert
object element = ConvertSimpleType(culture, value, destinationElementType);
IList converted = Array.CreateInstance(destinationElementType, );
converted[] = element;
return converted;
}
}
else if (valueAsArray != null)
{
// case 3: destination type is single element but source is array, so extract first element + convert
if (valueAsArray.Length > )
{
value = valueAsArray.GetValue();
return ConvertSimpleType(culture, value, destinationType);
}
else
{
// case 3(a): source is empty array, so can't perform conversion
return null;
}
}
// case 4: both destination + source type are single elements, so convert
return ConvertSimpleType(culture, value, destinationType);
}

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

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

3. 单一类型转换

再来看一下ConvertSimpleType的代码:

 private static object ConvertSimpleType(CultureInfo culture, object value, Type destinationType)
{
if (value == null || destinationType.IsInstanceOfType(value))
{
return value;
} // if this is a user-input value but the user didn't type anything, return no value
string valueAsString = value as string;
if (valueAsString != null && String.IsNullOrWhiteSpace(valueAsString))
{
return null;
} // In case of a Nullable object, we extract the underlying type and try to convert it.
Type underlyingType = Nullable.GetUnderlyingType(destinationType); if (underlyingType != null)
{
destinationType = underlyingType;
} // String doesn't provide convertibles to interesting types, and thus it will typically throw rather than succeed.
if (valueAsString == null)
{
// If the source type implements IConvertible, try that first
IConvertible convertible = value as IConvertible;
if (convertible != null)
{
try
{
return convertible.ToType(destinationType, culture);
}
catch
{
}
}
} // Last resort, look for a type converter
TypeConverter converter = TypeDescriptor.GetConverter(destinationType);
bool canConvertFrom = converter.CanConvertFrom(value.GetType());
if (!canConvertFrom)
{
converter = TypeDescriptor.GetConverter(value.GetType());
}
if (!(canConvertFrom || converter.CanConvertTo(destinationType)))
{
// EnumConverter cannot convert integer, so we verify manually
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);
}
}

  这里也考虑了几种情况转换
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的定义

 public static class ValueProviderFactories
{
private static readonly ValueProviderFactoryCollection _factories = new ValueProviderFactoryCollection()
{
new ChildActionValueProviderFactory(),
new FormValueProviderFactory(),
new JsonValueProviderFactory(),
new RouteDataValueProviderFactory(),
new QueryStringValueProviderFactory(),
new HttpFileCollectionValueProviderFactory(),
}; public static ValueProviderFactoryCollection Factories
{
get { return _factories; }
}
}

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

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

 public sealed class ChildActionValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
} return new ChildActionValueProvider(controllerContext);
}
}

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

 public sealed class FormValueProviderFactory : ValueProviderFactory
{
private readonly UnvalidatedRequestValuesAccessor _unvalidatedValuesAccessor; public FormValueProviderFactory()
: this(null)
{
} // For unit testing
internal FormValueProviderFactory(UnvalidatedRequestValuesAccessor unvalidatedValuesAccessor)
{
_unvalidatedValuesAccessor = unvalidatedValuesAccessor ?? (cc => new UnvalidatedRequestValuesWrapper(cc.HttpContext.Request.Unvalidated));
} public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
} return new FormValueProvider(controllerContext, _unvalidatedValuesAccessor(controllerContext));
}
}

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

  public sealed class JsonValueProviderFactory : ValueProviderFactory
{
private static void AddToBackingStore(EntryLimitedDictionary backingStore, string prefix, object value)
{
IDictionary<string, object> d = value as IDictionary<string, object>;
if (d != null)
{
foreach (KeyValuePair<string, object> entry in d)
{
AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
}
return;
} IList l = value as IList;
if (l != null)
{
for (int i = ; i < l.Count; i++)
{
AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
}
return;
} // primitive
backingStore.Add(prefix, value);
} private static object GetDeserializedObject(ControllerContext controllerContext)
{
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
// not JSON request
return null;
} StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
string bodyText = reader.ReadToEnd();
if (String.IsNullOrEmpty(bodyText))
{
// no JSON data
return null;
} JavaScriptSerializer serializer = new JavaScriptSerializer();
object jsonData = serializer.DeserializeObject(bodyText);
return jsonData;
} public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
} object jsonData = GetDeserializedObject(controllerContext);
if (jsonData == null)
{
return null;
} Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
EntryLimitedDictionary backingStoreWrapper = new EntryLimitedDictionary(backingStore);
AddToBackingStore(backingStoreWrapper, String.Empty, jsonData);
return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
} private static string MakeArrayKey(string prefix, int index)
{
return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
} private static string MakePropertyKey(string prefix, string propertyName)
{
return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
} private class EntryLimitedDictionary
{
private static int _maximumDepth = GetMaximumDepth();
private readonly IDictionary<string, object> _innerDictionary;
private int _itemCount = ; public EntryLimitedDictionary(IDictionary<string, object> innerDictionary)
{
_innerDictionary = innerDictionary;
} public void Add(string key, object value)
{
if (++_itemCount > _maximumDepth)
{
throw new InvalidOperationException(MvcResources.JsonValueProviderFactory_RequestTooLarge);
} _innerDictionary.Add(key, value);
} private static int GetMaximumDepth()
{
NameValueCollection appSettings = ConfigurationManager.AppSettings;
if (appSettings != null)
{
string[] valueArray = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers");
if (valueArray != null && valueArray.Length > )
{
int result;
if (Int32.TryParse(valueArray[], out result))
{
return result;
}
}
} return ; // Fallback default
}
}
}

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

  public sealed class RouteDataValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
} return new RouteDataValueProvider(controllerContext);
}
}

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

 public sealed class QueryStringValueProviderFactory : ValueProviderFactory
{
private readonly UnvalidatedRequestValuesAccessor _unvalidatedValuesAccessor; public QueryStringValueProviderFactory()
: this(null)
{
} // For unit testing
internal QueryStringValueProviderFactory(UnvalidatedRequestValuesAccessor unvalidatedValuesAccessor)
{
_unvalidatedValuesAccessor = unvalidatedValuesAccessor ?? (cc => new UnvalidatedRequestValuesWrapper(cc.HttpContext.Request.Unvalidated));
} public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
} return new QueryStringValueProvider(controllerContext, _unvalidatedValuesAccessor(controllerContext));
}
}

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

 public sealed class HttpFileCollectionValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
} return new HttpFileCollectionValueProvider(controllerContext);
}
}

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

ASP.NET MVC5学习笔记之Action参数模型绑定值提供体系的更多相关文章

  1. ASP.NET MVC5学习笔记之Action参数模型绑定基本过程

    当我们在Controller中定义一个Action,通常会定义一个或多个参数,每个参数称为一个模型,ASP.NET MVC框架提供了一种机制称为模型绑定,会尝试自动从请求的信息中实例化每一个模型并赋值 ...

  2. ASP.NET MVC5学习笔记之Action参数模型绑定之模型元数据和元数据提供

    一. 元数据描述类型ModelMetadata 模型元数据是对Model的描述信息,在ASP.NET MVC框架中有非常重要的作用,在模型绑定,模型验证,模型呈现等许多地方都有它的身影.描述Model ...

  3. ASP.NET MVC5学习笔记01

    由于之前在项目中也使用MVC进行开发,但是具体是那个版本就不是很清楚了,但是我觉得大体的思想是相同的,只是版本高的在版本低的基础上增加了一些更加方便操作的东西.下面是我学习ASP.NET MVC5高级 ...

  4. ASP.NET MVC5学习笔记之Filter提供体系

    前面我们介绍了Filter的基本使用,但各种Filter要在合适的时机运行起来,需要预先准备好,现在看看ASP.NET MVC框架是怎么做的. 一.Filter集合 在ControlerActionI ...

  5. ASP.NET MVC5学习笔记之Controller同步执行架构分析

    在开始之前,声明一下,由于ASP.NET MVC5正式发布了,后面的分析将基于ASP.NET MVC5最新的源代码.在前面的内容我们分析了怎样根据路由信息来确定Controller的类型,并最终生成C ...

  6. ASP.NET MVC5 学习笔记-1 控制器、路由、返回类型、选择器、过滤器

    [TOC] 1. Action 1.1 新建项目 新建项目->Web->Asp.net Web应用程序,选择MVC,选择添加测试. 在解决方案上右键,选择"管理NuGet程序包& ...

  7. ASP.NET MVC5学习笔记之Filter基本介绍

    Filter是ASP.NET MVC框架提供的基于AOP(面向方面)设计,提供在Action执行前后做一些非业务逻辑通用处理,如用户验证,缓存等.现在来看看Filter相关的一些类型信息. 一.基本类 ...

  8. ASP.NET MVC5 学习笔记-2 Razor

    1. Razor @*注释*@ 你在用 @Request.Browser.Browser, 发送邮件给support@qq.com, 转义@@qq @{ var amounts = new List& ...

  9. ASP.NET MVC5 学习笔记-3 Model

    1. Model 1.1 添加一个模型 注意,添加属性时可以输入"prop",会自动输入代码段. public class CheckoutAccount { public int ...

随机推荐

  1. JQuery基础教程:事件(下)

     事件传播     为了说明不可单击的页面元素处理单击事件的能力,例如样式转换器中包含按钮的div元素或者兄弟元素h3,我们来实现一个鼠标指针进入元素和离开元素时的效果,首先需要添加一种翻转状态,表明 ...

  2. 初探appium之元素定位(1)

    无论是selenium还是appium,元素定位都是我们开始实现自动化面临的第一个问题.selenium还好,我们可以在浏览器的调试页面进行元素定位还是蛮方便的.那么appium怎么做呢? 我看到很多 ...

  3. 酷我音乐API

    今天把酷我音乐API分享给大家: 歌曲搜索API:http://search.kuwo.cn/r.s?all={0}&ft=music& itemset=web_2013&cl ...

  4. SASS使用总结

    简单用法: 变量 sass中可以定义变量,方便统一修改和维护. //sass style $fontStack: Helvetica, sans-serif; $primaryColor: #333; ...

  5. Laxcus大数据管理系统2.0(10)- 第八章 安全

    第八章 安全 由于安全问题对大数据系统乃至当前社会的重要性,我们在Laxcus 2.0版本实现了全体系的安全管理策略.同时我们也考虑到系统的不同环节对安全管理的需求是不一样的,所以有选择地做了不同的安 ...

  6. oracle通过DBlink连接mysql(MariaDB)

    1.安装先装 mysql-connector-odbc(或 mariadb-connector-odbc )和unixODBChttps://downloads.mariadb.org/mariadb ...

  7. Android之EventBus使用详解

    一.概述 当Android项目越来越庞大的时候,应用的各个部件之间的通信变得越来越复杂,例如:当某一条件发生时,应用中有几个部件对这个消息感兴趣,那么我们通常采用的就是观察者模式,使用观察者模式有一个 ...

  8. 视频运动motion vector获取。

    http://victor.csie.org/blog/archives/362------------------------------------------------------------ ...

  9. Lamp源码包安装实录

    Lamp源码包安装实录 附件中是安装步骤,下载站点里包含视频(http://down.51cto.com/data/460776) 本文出自 "李晨光原创技术博客" 博客,请务必保 ...

  10. 简化对象extend拓展

    发现对对象继承或拷贝的时候,总是要$点来点去好麻烦,我的解决办法如下: (function(){ Object.prototype.extend = function(o){ $.extend(tru ...