熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

NameValueCollectionValueProvider

2013-11-13 10:21:05  來源: .NET編程 
    在進行Model綁定過程中需要根據基於Action方法參數的綁定上下文從請求數據中提取相應的數據以提供相應的數據具體來說Model綁定的數據具有多個來源可能來源於Post的表單或者JSON字符串或者來源於當前的路由數據也可能來源於請求地址的插敘字符串ASPNET MVC將這種基於不同數據來源的數據獲取/提供機制實現在一個叫做ValueProvider的組件中
   
    一IValueProvider與ValueProviderResult
   
    一般來講一個ValueProvider采用的數據源是一個字典類型的數據結構我們通過它從這個字典中獲取一個Key與當前綁定上下文匹配的值ValueProvider實現了具有如下定義的接口IValueProviderGetValue方法根據指定的Key從數據源中獲取對應的值對象這個Key是基於當前綁定上下文的這個Key和存在於數據源中對應數據條目的Key可能並非完全一致後者可能在前者基礎上添加相應的前綴而ContainsPrefix方法用於判斷數據源字典的Key是否具有指定的前綴
   
    : public interface IValueProvider
   
    : {
   
    :     bool ContainsPrefix(string prefix)
   
    :     ValueProviderResult GetValue(string key)
   
    : }
   
    IValueProvider的GetValue返回的是一個ValueProviderResult對象我們可以將ValueProviderResult看成是對ValueProvider提供對象的封裝如下面的代碼片斷所示ValueProviderResult具有三個只讀屬性其中RawValue表示原始的值對象而AttemptedValue表示以值對象的字符串表示該屬性主要用於顯示
   
    : [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; }
   
    : }
   
    ValueProviderResult提供了兩個ConvertTo方法重載以實現向指定目標類型的轉換某些類型的格式化行為依賴於相應的語言文化(比如時間日期和貨幣等)而這個輔助格式湖的語言文化信息通過Culture屬性表示其中第一個ValueProviderResult方法重載通過屬性Culture表示的語言文化進行類型轉化


   
    二NameValueCollectionValueProvider
   
    前面已經說過Model數據源一般具有類似於字典的結構而NameValueCollection可以表示為Key不具有唯一性的字典將NameValueCollection對象作為數據源的ValueProvider通過具有如下定義的NameValueCollectionValueProvider類型表示表示數據源的NameValueCollection對象在構造函數中指定構造函數的另一個CultureInfo類型的參數表示服務於數據轉換的語言文化信息
   
    : 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)
   
    : }
   
    從上面的代碼片斷我們可以看到除了IValueProvider接口NameValueCollectionValueProvider還實現了IEnumerableValueProvider和IUnvalidatedValueProvider兩個接口顧名思義IEnumerableValueProvider主要用於針對目標類型為集合的數據提供方法GetKeysFromPrefix以一字典的形式返回具有指定前綴的Key在默認的情況下在進行數據提供的同時會對數據進行驗證而IUnvalidatedValueProvider接口提供了一個額外的GetValue方法是我們可以忽略對數據的驗證
   
    三兩種前綴形式
   
    輔助實現Model綁定的數據提供機制是以Model元數據為基礎的通過《初識Model元數據》我們知道用於描述一個復雜數據類型的Model元數據具有一個樹型的層次化結構而作為數據源的NameValueCollection卻是一個扁平的結構兩者之前的匹配通過前綴來表示舉個簡單的例子假設通過NameValueCollectionValueProvider提供對象的目標類型為具有如下定義的Contact表示聯系地址的屬性是一個復雜類型Address所以針對Contact類型的Model元數據樹具有兩個層級
   
    : 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; }
   
    : }


   
    由於NameValueCollection中每個元數據的值都是一個字符串所以不可能單獨表示一個復雜類型復雜類型對象需要通過多個元素值組裝而成如果通過NameValueCollectionValueProvider來初始化一個完整的Contact對象表示數據源的NameValueCollection至少需要包含個元素分別針對Contact除Address屬性的三個屬性值和作為Address的四個屬性值兩類元素在NameValueCollection中通過基於屬性的前綴來區分具體的結構如下所示
   
    : Name:Foo
   
    : PhoneNo:
   
    : EmailAddress:
   
    : AddressProvince: 江蘇
   
    : AddressCity: 蘇州
   
    : AddressDistrict: 工業園區
   
    : AddressStreet: 星湖街
   
    將點號()作為分隔符的前綴除了表示基於屬性的層級關系之外還可以用於數據篩選如下面的代碼片斷所示我們在ContactController中定義了一個用於添加聯系人的AddContacts它具有兩個Contact類型的參數foo和bar表示添加的兩個不同的聯系人
   
    : public class ContactController
   
    : {
   
    :     public void AddContacts(Contact foo Contact bar)
   
    :     {
   
    :         //省略實現
   
    :     }
   
    : }
   
    如果我們采用NameValueCollectionValueProvider來提供作為AddContacts方法參數的兩個Contact對象保存在NameValueCollection的數據元素必須能夠與它們進行合理映射一般情況下這可以通過針對參數名的前綴來實現具體數據結構如下所示
   
    : fooName:Foo
   
    : fooPhoneNo:
   
    : fooEmailAddress:
   
    : fooAddressProvince: 江蘇
   
    : fooAddressCity: 蘇州
   
    : fooAddressDistrict: 工業園區
   
    : fooAddressStreet: 星湖街
   
    :
   
    : barName:Bar
   
    : barPhoneNo:
   
    : barEmailAddress:
   
    : barAddressProvince: 江蘇
   
    : barAddressCity: 蘇州
   
    : barAddressDistrict: 工業園區
   
    : barAddressStreet: 機場路
   
    除了采用基於的前綴之外數組或者集合類型的數據源元素可以采用基於索引的前綴這樣的前綴通過方括號[]表示如下的數據結構就可以表示包含兩個元素的Contact數組或者集合
   
    : []Name:Foo
   
    : []PhoneNo:
   
    : []EmailAddress:
   
    : …
   
    : []Name:Bar
   
    : []PhoneNo:
   
    : []EmailAddress:
   
    : …
   
    :
   
    除了采用數字作為索引之前我們還可以按照如下的方式通過文字作為索引針對兩種不同形式的索引的Model綁定機制有所不同我們會在後續的部分予以講述
   
    : [foo]Name:Foo
   
    : [foo]PhoneNo:
   
    : [foo]EmailAddress:
   
    : …
   
    : [bar]Name:Bar
   
    : [bar]PhoneNo:
   
    : [bar]EmailAddress:
   
    : …


   
    如果數據源元素針對不同的目標集合對象同樣需要采用相應的前綴予以區分相面的數據結構可以看成是針對兩個Contact列表(first和second)的數據源
   
    : first[]Name:Zhao
   
    : first[]PhoneNo:
   
    : first[]EmailAddress:zhao
   
    : …
   
    : first[]Name:Qian
   
    : first[]PhoneNo:
   
    : first[]EmailAddress:
   
    : …
   
    :
   
    : second[]Name:Sun
   
    : second[]PhoneNo:
   
    : second[]EmailAddress:
   
    : …
   
    : second[]Name:Li
   
    : second[]PhoneNo:
   
    : second[]EmailAddress:
   
    四實例演示返回指定前綴的Key
   
    在了解兩種不同類型的前綴之後我們來關注一下NameValueCollectionValueProvider實現的GetKeysFromPrefix方法從該方法的定義可以看出它返回的是一個IDictionary<string string>對象但是這個對象具有怎樣的數據呢?我們為此來進行一個實例演示在通過Visual Studio的ASPNET MVC項目模板創建的空Web應用中我們定義了如下一個默認的HomeController在Action方法Index中我們創建了一個NameValueCollection對象並針對它創建一個NameValueCollectionValueProvider
   
    : public class HomeController : Controller
   
    : {
   
    :     public void Index()
   
    :     {
   
    :         NameValueCollection datasource = new NameValueCollection()
   
    :         datasourceAdd(fooName Foo
   
    :         datasourceAdd(fooPhoneNo
   
    :         datasourceAdd(fooEmailAddress
   
    :         datasourceAdd(fooAddressProvince 江蘇
   
    :         datasourceAdd(fooAddressCity 蘇州
   
    :         datasourceAdd(fooAddressDistrict 工業園區
   
    :         datasourceAdd(fooAddressStreet 星湖街
   
    :         NameValueCollectionValueProvider valueProvider = new NameValueCollectionValueProvider(datasource CultureInfoInvariantCulture)
   
    :
   
    :         var keyDictionary = valueProviderGetKeysFromPrefix(foo
   
    :         ResponseWrite(foo<br/>
   
    :         foreach (var item in keyDictionary)
   
    :         {
   
    :             ResponseWrite(stringFormat({}: {}<br/> itemKey itemValue))
   
    :         }
   
    :
   
    :         keyDictionary = valueProviderGetKeysFromPrefix(fooAddress
   
    :         ResponseWrite(<br/>fooAddress<br/>
   
    :         foreach (var item in keyDictionary)
   
    :         {
   
    :             ResponseWrite(stringFormat({}: {}<br/> itemKey itemValue))
   
    :         }
   
    :     }
   
    : }


   
    通過上面的代碼片斷可以看出作為NameValueCollectionValueProvider的數據元素是按照Contact類型的屬性定義來添加的我們分別將foofooAddress作為前綴返回以此作為前綴的Key運行該程序後會在浏覽器上得到如下的輸出結果我們可以看到對於針對指定前綴返回的字典對象其Key和Value的不同之處在於前者沒有包含指定的前綴而後者包含此外字典對象包含的元素全部處於同一級別foo指定為前綴時返回的元素針對於Contact的四個屬性雖然NameValueCollection中並不包含一個名為fooAddress的元素但是依然會將其單獨作為以foo為前綴的Key
   
    : foo
   
    : Name            : fooName
   
    : PhoneNo         : fooPhoneNo
   
    : EmailAddress    : fooEmailAddress
   
    : Address         : fooAddress
   
    :
   
    : fooAddress
   
    : Province        : fooAddressProvince
   
    : City            : fooAddressCity
   
    : District        : fooAddressDistrict
   
    : Street          : fooAddressStreet
   
    接下來我們采用相應的方式來演示基於索引的前綴為此我們將HomeController的Index反方法進行了如下的改寫作為數據源的NameValueCollection對象針對一個包含兩個元素的Contact集合前綴first可以作為集合對象的名稱
   
    : public class HomeController : Controller
   
    : {
   
    :     public void Index()
   
    :     {
   
    :         NameValueCollection datasource = new NameValueCollection()
   
    :         datasourceAdd(first[]Name Foo
   
    :         datasourceAdd(first[]PhoneNo
   
    :         datasourceAdd(first[]EmailAddress
   
    :
   
    :         datasourceAdd(first[]Name Bar
   
    :         datasourceAdd(first[]PhoneNo
   
    :         datasourceAdd(first[]EmailAddress
   
    :         NameValueCollectionValueProvider valueProvider = new NameValueCollectionValueProvider(datasource CultureInfoInvariantCulture)
   
    :
   
    :         var keyDictionary = valueProviderGetKeysFromPrefix(first
   
    :         ResponseWrite(first<br/>
   
    :         foreach (var item in keyDictionary)
   
    :         {
   
    :             ResponseWrite(stringFormat({}: {}<br/> itemKey itemValue))
   
    :         }
   
    :
   
    :         keyDictionary = valueProviderGetKeysFromPrefix(first[]
   
    :         ResponseWrite(<br/>first[]<br/>
   
    :         foreach (var item in keyDictionary)
   
    :         {
   
    :             ResponseWrite(stringFormat({}: {}<br/> itemKey itemValue))
   
    :         }
   
    :
   
    :         keyDictionary = valueProviderGetKeysFromPrefix(first[]
   
    :         ResponseWrite(<br/>first[]<br/>
   
    :         foreach (var item in keyDictionary)
   
    :         {
   
    :             ResponseWrite(stringFormat({}: {}<br/> itemKey itemValue))
   
    :         }
   
    :     }
   
    : }


   
    我們分別針對三個前綴firstfirst[]first[]獲取相應字典對象並將其Key和Value呈現出來該程序執行之後會在浏覽器中產生如下的輸出如果我們將[]視為和一樣的分割符GetKeysFromPrefix針對索引作為前綴的規則與基於前綴的規則沒有本質的區別
   
    : first
   
    : : first[]
   
    : : first[]
   
    :
   
    : first[]
   
    : Name        : first[]Name
   
    : PhoneNo     : first[]PhoneNo
   
    : EmailAddress: first[]EmailAddress
   
    :
   
    : first[]
   
    : Name        : first[]Name
   
    : PhoneNo     : first[]PhoneNo
   
    : EmailAddress: first[]EmailAddress
   
    五FormValueProvider與QueryStringValueProvider
   
    在ASPNET MVC 應用編程接口中NameValueCollectionValueProvider具有兩個繼承者即FormValueProvider和QueryStringValueProvider對於FormValueProvider來說最終作為數據源的NameValueCollection對象通過請求表單創建Name和Value分別來源於表單元素的名稱和值它的定義基本上可以通過如下的代碼表示(實際定義有所差異)
   
    : public sealed class FormValueProvider : NameValueCollectionValueProvider
   
    : {
   
    :     public FormValueProvider(ControllerContext controllerContext)
   
    :         : base(controllerContextRequestContextHttpContextRequestForm CultureInfoCurrentCulture)
   
    :     { }
   
    : }
   
    對於QueryStringValueProvider來說無須多說其作為數據源的NameValueCollection對象愛那個自然來源於請求的查詢字符串其定義基本上可以通過如下的代碼表示(實際定義有所差異)
   
    : public sealed class QueryStringValueProvider: NameValueCollectionValueProvider
   
    : {
   
    :     public NameValueCollection(ControllerContext controllerContext)
   
    :         : base(controllerContextRequestContextHttpContextRequestQueryString CultureInfoCurrentCulture)
   
    :     { }
   
    : }


From:http://tw.wingwit.com/Article/program/net/201311/13339.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.