您的位置:首页 > 编程语言 > ASP

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

2012-05-18 17:34 330 查看
[code] public interface IValueProvider

{

bool ContainsPrefix(string prefix);

ValueProviderResult GetValue(string key);

}

[/code]
[/code]
IValueProvider的GetValue返回的是一个ValueProviderResult对象,我们可以将ValueProviderResult看成是对ValueProvider提供对象的封装。如下面的代码片断所示,ValueProviderResult具有三个只读属性,其中RawValue表示原始的值对象。而AttemptedValue表示以值对象的字符串表示,该属性主要用于显示。

[code]
[code] [Serializable]

public class ValueProviderResult

{

public ValueProviderResult(object rawValue, string attemptedValue, CultureInfo culture);

public object ConvertTo(Type type);

public virtual object ConvertTo(Type type, CultureInfo culture);


 public string AttemptedValue{ get;}

 public CultureInfo Culture{ get;}

 public object RawValue{ get;}

}

[/code]
[/code]
ValueProviderResult提供了两个ConvertTo方法重载以实现向指定目标类型的转换。某些类型的格式化行为依赖于相应的语言文化(比如时间、日期和货币等),而这个辅助格式湖的语言文化信息通过Culture属性表示。其中第一个ValueProviderResult方法重载通过属性Culture表示的语言文化进行类型转化。

二、NameValueCollectionValueProvider

前面已经说过,Model数据源一般具有类似于字典的结构,而NameValueCollection可以表示为Key不具有唯一性的字典,将NameValueCollection对象作为数据源的ValueProvider通过具有如下定义的NameValueCollectionValueProvider类型表示。表示数据源的NameValueCollection对象在构造函数中指定,构造函数的另一个CultureInfo类型的参数表示服务于数据转换的语言文化信息。

[code]
[code] public class NameValueCollectionValueProvider : IUnvalidatedValueProvider, IEnumerableValueProvider, IValueProvider

{ 

 //其他成员

 public NameValueCollectionValueProvider(NameValueCollection collection,CultureInfo culture);


 public virtual bool ContainsPrefix(string prefix);

 public virtual IDictionary<string, string> GetKeysFromPrefix(string prefix);

 public virtual ValueProviderResult GetValue(string key);

 public virtual ValueProviderResult GetValue(string key, bool skipValidation);

}


public interface IEnumerableValueProvider : IValueProvider

{

 IDictionary<string, string> GetKeysFromPrefix(string prefix);

}


public interface IUnvalidatedValueProvider : IValueProvider

{

 ValueProviderResult GetValue(string key, bool skipValidation);

}

[/code]
[/code]
从上面的代码片断我们可以看到,除了IValueProvider接口,NameValueCollectionValueProvider还实现了IEnumerableValueProvider和IUnvalidatedValueProvider两个接口。顾名思义,IEnumerableValueProvider主要用于针对目标类型为集合的数据提供,方法GetKeysFromPrefix以一字典的形式返回具有指定前缀的Key。在默认的情况下,在进行数据提供的同时会对数据进行验证,而IUnvalidatedValueProvider接口提供了一个额外的GetValue方法是我们可以忽略对数据的验证。

三、两种前缀形式

辅助实现Model绑定的数据提供机制是以Model元数据为基础的,通过《初识Model元数据》我们知道用于描述一个复杂数据类型的Model元数据具有一个树型的层次化结构,而作为数据源的NameValueCollection却是一个“扁平”的结构,两者之前的匹配通过前缀来表示。举个简单的例子,假设通过NameValueCollectionValueProvider提供对象的目标类型为具有如下定义的Contact。表示联系地址的属性是一个复杂类型Address,所以针对Contact类型的Model元数据树具有两个层级。

[code]
[code] public class Contact

{

 public string Name{ get; set;}

 public string PhoneNo{ get; set;}

 public string EmailAddress{ get; set;}

 public Address Address{ get; set;}

}

public class Address

{

 public string Province{ get; set;}

 public string City{ get; set;}

 public string District{ get; set;}

 public string Street{ get; set;}

}

[/code]
[/code]
由于NameValueCollection中每个元数据的值都是一个字符串,所以不可能单独表示一个复杂类型,复杂类型对象需要通过多个元素值组装而成。如果通过NameValueCollectionValueProvider来初始化一个完整的Contact对象,表示数据源的NameValueCollection至少需要包含7个元素,分别针对Contact除Address属性的三个属性值和作为Address的四个属性值,两类元素在NameValueCollection中通过基于属性的前缀来区分,具体的结构如下所示。

[code]
[code] Name:Foo

PhoneNo:123456789

EmailAddress:Foomail.com

Address.Province: 江苏

Address.City: 苏州

Address.District: 工业园区

Address.Street: 星湖街328号

[/code]
[/code]
将点号(.)作为分隔符的前缀除了表示基于属性的层级关系之外,还可以用于数据筛选。如下面的代码片断所示,我们在ContactController中定义了一个用于添加联系人的AddContacts,它具有两个Contact类型的参数foo和bar,表示添加的两个不同的联系人。

[code]
[code] public class ContactController

{

 public void AddContacts(Contact foo, Contact bar)

{ 

 //省略实现

}

}

[/code]
[/code]
如果我们采用NameValueCollectionValueProvider来提供作为AddContacts方法参数的两个Contact对象,保存在NameValueCollection的数据元素必须能够与它们进行合理映射。一般情况下这可以通过针对参数名的前缀来实现,具体数据结构如下所示。

[code]
[code] foo.Name:Foo

foo.PhoneNo:123456789

foo.EmailAddress:Foo@gmail.com

foo.Address.Province: 江苏

foo.Address.City: 苏州

foo.Address.District: 工业园区

foo.Address.Street: 星湖街328号


bar.Name:Bar

bar.PhoneNo:987654321

bar.EmailAddress:Bar@gmail.com

bar.Address.Province: 江苏

bar.Address.City: 苏州

bar.Address.District: 工业园区

bar.Address.Street: 机场路328号

[/code]
[/code]
除了采用基于“.”的前缀之外,数组或者集合类型的数据源元素可以采用基于“索引”的前缀,这样的前缀通过方括号“[]”表示,如下的数据结构就可以表示包含两个元素的Contact数组或者集合。

[code]
[code] [0].Name:Foo

[0].PhoneNo:123456789

[0].EmailAddress:Foo@gmail.com

...

[1].Name:Bar

[1].PhoneNo:987654321

[1].EmailAddress:Bar@gmail.com

...


[/code]
[/code]
除了采用数字作为索引之前,我们还可以按照如下的方式通过文字作为索引。针对两种不同形式的索引的Model绑定机制有所不同,我们会在后续的部分予以讲述。

[code]
[code] [foo].Name:Foo

[foo].PhoneNo:123456789

[foo].EmailAddress:Foo@gmail.com

...

[bar].Name:Bar

[bar].PhoneNo:987654321

[bar].EmailAddress:Bar@gmail.com

...

[/code]
[/code]
如果数据源元素针对不同的目标集合对象,同样需要采用相应的前缀予以区分,相面的数据结构可以看成是针对两个Contact列表(first和second)的数据源。

[code]
[code]first[0].Name:Zhao

first[0].PhoneNo:12

first[0].EmailAddress:zhao @gmail.com

...

first[1].Name:Qian

first[1].PhoneNo:34

first[1].EmailAddress:qian@gmail.com

...


second[0].Name:Sun

second[0].PhoneNo:56

second[0].EmailAddress:sun@gmail.com

...

second[1].Name:Li

second[1].PhoneNo:78

second[1].EmailAddress:li@gmail.com

[/code]
[/code]

四、实例演示:返回指定前缀的Key

在了解两种不同类型的前缀之后,我们来关注一下NameValueCollectionValueProvider实现的GetKeysFromPrefix方法。从该方法的定义可以看出它返回的是一个IDictionary<string, string>对象,但是这个对象具有怎样的数据呢?我们为此来进行一个实例演示。在通过Visual Studio的ASP.NET MVC项目模板创建的空Web应用中,我们定义了如下一个默认的HomeController。在Action方法Index中我们创建了一个NameValueCollection对象,并针对它创建一个NameValueCollectionValueProvider.

[code]
[code] public class HomeController : Controller

{

 public void Index()

{

 NameValueCollection datasource = new NameValueCollection();

 datasource.Add("foo.Name", "Foo");

 datasource.Add("foo.PhoneNo", "123456789");

 datasource.Add("foo.EmailAddress", "Foo@gmail.com");

 datasource.Add("foo.Address.Province", "江苏");

 datasource.Add("foo.Address.City", "苏州");

 datasource.Add("foo.Address.District", "工业园区");

 datasource.Add("foo.Address.Street", "星湖街328号");

 NameValueCollectionValueProvider valueProvider = new NameValueCollectionValueProvider(datasource, CultureInfo.InvariantCulture);


 var keyDictionary = valueProvider.GetKeysFromPrefix("foo");

 Response.Write("foo<br/>");

 foreach (var item in keyDictionary)

{

 Response.Write(string.Format("{0}:{1}<br/>", item.Key, item.Value));

}


 keyDictionary = valueProvider.GetKeysFromPrefix("foo.Address");

 Response.Write("<br/>foo.Address<br/>");

 foreach (var item in keyDictionary)

{

 Response.Write(string.Format("{0}:{1}<br/>", item.Key, item.Value));

}

}

}

[/code]
[/code]
通过上面的代码片断可以看出,作为NameValueCollectionValueProvider的数据元素是按照Contact类型的属性定义来添加的。我们分别将“foo”和“foo.Address”作为前缀返回以此作为前缀的Key。运行该程序后会在浏览器上得到如下的输出结果。我们可以看到对于针对指定前缀返回的字典对象,其Key和Value的不同之处在于前者没有包含指定的前缀而后者包含。此外,字典对象包含的元素全部处于同一级别,将“foo”指定为前缀时返回的元素针对于Contact的四个属性。虽然NameValueCollection中并不包含一个名为“foo.Address”的元素,但是依然会将其单独作为以“foo”为前缀的Key。

[code]
[code] foo

 Name: foo.Name

 PhoneNo : foo.PhoneNo

 EmailAddress: foo.EmailAddress

 Address :foo.Address


foo.Address

 Province:foo.Address.Province

 City:foo.Address.City

 District:foo.Address.District

 Street:foo.Address.Street

[/code]
[/code]
接下来我们采用相应的方式来演示基于索引的前缀,为此我们将HomeController的Index反方法进行了如下的改写。作为数据源的NameValueCollection对象针对一个包含两个元素的Contact集合,前缀“first”可以作为集合对象的名称。

[code]
[code] public class HomeController : Controller

{

 public void Index()

{

 NameValueCollection datasource = new NameValueCollection();

 datasource.Add("first[0].Name", "Foo");

 datasource.Add("first[0].PhoneNo", "123456789");

 datasource.Add("first[0].EmailAddress", "Foo@gmail.com");


 datasource.Add("first[1].Name", "Bar");

 datasource.Add("first[1].PhoneNo", "987654321");

 datasource.Add("first[1].EmailAddress", "Bar@gmail.com");

 NameValueCollectionValueProvider valueProvider = new NameValueCollectionValueProvider(datasource, CultureInfo.InvariantCulture);


 var keyDictionary = valueProvider.GetKeysFromPrefix("first");

 Response.Write("first<br/>");

 foreach (var item in keyDictionary)

{

 Response.Write(string.Format("{0}:{1}<br/>", item.Key, item.Value));

}


 keyDictionary = valueProvider.GetKeysFromPrefix("first[0]");

 Response.Write("<br/>first[0]<br/>");

 foreach (var item in keyDictionary)

{

 Response.Write(string.Format("{0}:{1}<br/>", item.Key, item.Value));

}


 keyDictionary = valueProvider.GetKeysFromPrefix("first[1]");

 Response.Write("<br/>first[1]<br/>");

 foreach (var item in keyDictionary)

{

 Response.Write(string.Format("{0}:{1}<br/>", item.Key, item.Value));

}

}

}

[/code]
[/code]
我们分别针对三个前缀“first”、“first[0]”和“first[1]”获取相应字典对象并将其Key和Value呈现出来。该程序执行之后会在浏览器中产生如下的输出,如果我们将“[”和“]”视为和”.”一样的分割符,GetKeysFromPrefix针对索引作为前缀的规则与基于“.”前缀的规则没有本质的区别。

[code]
[code] first

0:first[0]

1:first[1]


first[0]

 Name:first[0].Name

 PhoneNo :first[0].PhoneNo

 EmailAddress:first[0].EmailAddress


first[1]

 Name:first[1].Name

 PhoneNo :first[1].PhoneNo

 EmailAddress:first[1].EmailAddress

[/code]
[/code]

五、FormValueProvider与QueryStringValueProvider

在ASP.NET MVC 应用编程接口中,NameValueCollectionValueProvider具有两个继承者,即FormValueProviderQueryStringValueProvider。对于FormValueProvider来说,最终作为数据源的NameValueCollection对象通过请求表单创建,Name和Value分别来源于表单元素的名称和值,它的定义基本上可以通过如下的代码表示(实际定义有所差异)。

[code]
[code] public sealed class FormValueProvider : NameValueCollectionValueProvider

{

 public FormValueProvider(ControllerContext controllerContext)

 : base(controllerContext.RequestContext.HttpContext.Request.Form, CultureInfo.CurrentCulture)

{}

}

[/code]
[/code]
对于QueryStringValueProvider来说,无须多说,其作为数据源的NameValueCollection对象爱那个自然来源于请求的查询字符串,其定义基本上可以通过如下的代码表示(实际定义有所差异)。

[code]
[code] public sealed class QueryStringValueProvider: NameValueCollectionValueProvider

{

 public NameValueCollection(ControllerContext controllerContext)

 : base(controllerContext.RequestContext.HttpContext.Request.QueryString, CultureInfo.CurrentCulture)

{}

}

[/code]
[/code]

ASP.NET MVC以ValueProvider为核心的值提供系统: NameValueCollectionValueProvider
ASP.NET MVC以ValueProvider为核心的值提供系统: DictionaryValueProvider
ASP.NET MVC以ValueProvider为核心的值提供系统: ValueProviderFactory
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: