当前位置:编程学习 > asp >>

ASP.NET MVC以ValueProvider为核心的值提供系统: NameValueCollectionValueProvider

在进行Model绑定过程中,需要根据基于Action方法参数的绑定上下文从请求数据中提取相应的数据以提供相应的数据。具体来说,Model绑定的数据具有多个来源,可能来源于Post的表单或者JSON字符串,或者来源于当前的路由数据,也可能来源于请求地址的插叙字符串。ASP.NET MVC将这种基于不同数据来源的数据获取/提供机制实现在一个叫做ValueProvider的组件中。
目录
一、IValueProvider与ValueProviderResult
二、NameValueCollectionValueProvider
三、两种前缀形式
四、实例演示:返回指定前缀的Key
五、FormValueProvider与QueryStringValueProvider
一、IValueProvider与ValueProviderResult
一般来讲,一个ValueProvider采用的数据源是一个字典类型的数据结构,我们通过它从这个字典中获取一个Key与当前绑定上下文匹配的值。ValueProvider实现了具有如下定义的接口IValueProvider,GetValue方法根据指定的Key从数据源中获取对应的值对象,这个Key是基于当前绑定上下文的。这个Key和存在于数据源中对应数据条目的Key可能并非完全一致,后者可能在前者基础上添加相应的前缀,而ContainsPrefix方法用于判断数据源字典的Key是否具有指定的前缀。
   1: public interface IValueProvider
   2: {
   3:     bool ContainsPrefix(string prefix);
   4:     ValueProviderResult GetValue(string key);
   5: }
IValueProvider的GetValue返回的是一个ValueProviderResult对象,我们可以将ValueProviderResult看成是对ValueProvider提供对象的封装。如下面的代码片断所示,ValueProviderResult具有三个只读属性,其中RawValue表示原始的值对象。而AttemptedValue表示以值对象的字符串表示,该属性主要用于显示。
   1: [Serializable]
   2: public class ValueProviderResult
   3: {   
   4:     public ValueProviderResult(object rawValue, string attemptedValue, CultureInfo culture);   
   5:     public object ConvertTo(Type type);
   6:     public virtual object ConvertTo(Type type, CultureInfo culture);
   7: 
   8:     public string AttemptedValue { get; }
   9:     public CultureInfo Culture { get; }
  10:     public object RawValue { get; }
  11: }
ValueProviderResult提供了两个ConvertTo方法重载以实现向指定目标类型的转换。某些类型的格式化行为依赖于相应的语言文化(比如时间、日期和货币等),而这个辅助格式湖的语言文化信息通过Culture属性表示。其中第一个ValueProviderResult方法重载通过属性Culture表示的语言文化进行类型转化。
二、NameValueCollectionValueProvider
前面已经说过,Model数据源一般具有类似于字典的结构,而NameValueCollection可以表示为Key不具有唯一性的字典,将NameValueCollection对象作为数据源的ValueProvider通过具有如下定义的NameValueCollectionValueProvider类型表示。表示数据源的NameValueCollection对象在构造函数中指定,构造函数的另一个CultureInfo类型的参数表示服务于数据转换的语言文化信息。
   1: public class NameValueCollectionValueProvider : IUnvalidatedValueProvider, IEnumerableValueProvider, IValueProvider
   2: {  
   3:     //其他成员
   4:     public NameValueCollectionValueProvider(NameValueCollection collection,  CultureInfo culture);
   5: 
   6:     public virtual bool ContainsPrefix(string prefix);
   7:     public virtual IDictionary<string, string> GetKeysFromPrefix(string prefix);
   8:     public virtual ValueProviderResult GetValue(string key);
   9:     public virtual ValueProviderResult GetValue(string key, bool skipValidation); 
  10: }
  11: 
  12: public interface IEnumerableValueProvider : IValueProvider
  13: {
  14:     IDictionary<string, string> GetKeysFromPrefix(string prefix);
  15: }
  16: 
  17: public interface IUnvalidatedValueProvider : IValueProvider
  18: {
  19:     ValueProviderResult GetValue(string key, bool skipValidation);
  20: }
从上面的代码片断我们可以看到,除了IValueProvider接口,NameValueCollectionValueProvider还实现了IEnumerableValueProvider和IUnvalidatedValueProvider两个接口。顾名思义,IEnumerableValueProvider主要用于针对目标类型为集合的数据提供,方法GetKeysFromPrefix以一字典的形式返回具有指定前缀的Key。在默认的情况下,在进行数据提供的同时会对数据进行验证,而IUnvalidatedValueProvider接口提供了一个额外的GetValue方法是我们可以忽略对数据的验证。
三、两种前缀形式
辅助实现Model绑定的数据提供机制是以Model元数据为基础的,通过《初识Model元数据》我们知道用于描述一个复杂数据类型的Model元数据具有一个树型的层次化结构,而作为数据源的NameValueCollection却是一个“扁平”的结构,两者之前的匹配通过前缀来表示。举个简单的例子,假设通过NameValueCollectionValueProvider提供对象的目标类型为具有如下定义的Contact。表示联系地址的属性是一个复杂类型Address,所以针对Contact类型的Model元数据树具有两个层级。
   1: public class Contact
   2: {
   3:     public string Name { get; set; }
   4:     public string PhoneNo { get; set; }
   5:     public string EmailAddress { get; set; }
   6:     public Address Address { get; set; }
   7: }
   8: public class Address
   9: {
  10:     public string Province { get; set; }
  11:     public string City { get; set; }
  12:     public string District { get; set; }
  13:     public string Street { get; set; }
  14: }
由于NameValueCollection中每个元数据的值都是一个字符串,所以不可能单独表示一个复杂类型,复杂类型对象需要通过多个元素值组装而成。如果通过NameValueCollectionValueProvider来初始化一个完整的Contact对象,表示数据源的NameValueCollection至少需要包含7个元素,分别针对Contact除Address属性的三个属性值和作为Address的四个属性值,两类元素在NameValueCollection中通过基于属性的前缀来区分,具体的结构如下所示。
   1: Name:Foo
   2: PhoneNo:123456789
   3: EmailAddress:Foomail.com
   4: Address.Province: 江苏
   5: Address.City: 苏州
   6: Address.District: 工业园区
   7: Address.Street: 星湖街328号
将点号(.)作为分隔符的前缀除了表示基于属性的层级关系之外,还可以用于数据筛选。如下面的代码片断所示,我们在ContactController中定义了一个用于添加联系人的AddContacts,它具有两个Contact类型的参数foo和bar,表示添加的两个不同的联系人。
   1: public class ContactController
   2: {
   3:     public void AddContacts(Contact foo, Contact bar)
   4:     {
   5:         //省略实现
   6:     }
   7: }
如果我们采用NameValueCollectionValueProvider来提供作为AddContacts方法参数的两个Contact对象,保存在NameValueCollection的数据元素必须能够与它们进行合理映射。一般情况下这可以通过针对参数名的前缀来实现,具体数据结构如下所示。
   1: foo.Name:Foo
   2: foo.PhoneNo:123456789
   3: foo.EmailAddress:Foo@gmail.com
   4: foo.Address.Province: 江苏
   5: foo.Address.City: 苏州
   6: foo.Address.District: 工业园区
   7: foo.Address.Street: 星湖街328号
   8: 

补充:Web开发 , ASP.Net ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,