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

ASP.NET MVC: DictionaryValueProvider

2013-11-13 10:30:59  來源: .NET編程 

  NameValueCollectionValueProvider采用一個NameValueCollection作為數據源DictionnaryValueProvider的數據源類型自然就是一個DictionnaryNameValueCollection和Dictionnary都是一個鍵值對的集合它們之間的不同之處在NameValueCollection運行元素具有相同的KeyDictionnary卻要求元素的Key具有唯一性
DictionaryValueProvider<TValue>
DictionnaryValueProvider的類型全名為SystemWebMvcDictionaryValueProvider<TValue>如下面的代碼片斷所示DictionaryValueProvider<TValue>實現了IEnumerableValueProvider和IValueProvider接口構造函數接受一個IDictionary<string TValue>對象該對象表示作為數據源的字典定義在DictionaryValueProvider<TValue>中所有方法的邏輯與定義在NameValueCollectionValueProvider中的同名方法並沒有本質區別
   : public class DictionaryValueProvider<TValue> : IEnumerableValueProvider IValueProvider
   : {
   :     public DictionaryValueProvider(IDictionary<string TValue> dictionary CultureInfo culture);
   :     public virtual bool ContainsPrefix(string prefix);
   :     public virtual IDictionary<string string> GetKeysFromPrefix(string prefix);
   :     public virtual ValueProviderResult GetValue(string key);
   : }

  RouteDataValueProvider
將當前路由數據作為數據源的RouteDataValueProvider繼承自DictionaryValueProvider<TValue>如下面的代碼片斷所示基於當前Controller上下文構建的RouteDataValueProvider直接將表示當前路由數據的RouteData對象的Values屬性(這是一個RouteValueDictionary對象)作為數據來源
   : public sealed class RouteDataValueProvider : DictionaryValueProvider<object>
   : {
   :     public RouteDataValueProvider(ControllerContext controllerContext) :
   :         base(controllerContextRouteDataValues CultureInfoInvariantCulture)
   :     {
   :     }
   : }

  HttpFileCollectionValueProvider
我們可以通過類型為file的輸入元素進行文件的上傳在表示HTTP請求的HttpRequestBase對象中上傳文件通過只讀屬性Files表示從下面的代碼片斷所示該屬性類型為HttpFileCollectionBase是一個元素類型為HttpPostedFileBase的集合
   : public abstract class HttpRequestBase
   : {  
   :     public virtual HttpFileCollectionBase Files { get; }
   : }
   : public abstract class HttpFileCollectionBase : NameObjectCollectionBase ICollection IEnumerable
   : {  
   :     public virtual string[] AllKeys { get; }
   :     public override int Count { get; }
   :     public virtual HttpPostedFileBase this[int index] { get; }
  :     public virtual HttpPostedFileBase this[string name] { get; }
  : }
  : public abstract class HttpPostedFileBase
  : {
  :     public virtual void SaveAs(string filename);
 
  :     public virtual int ContentLength { get; }
  :     public virtual string ContentType { get; }
  :     public virtual string FileName { get; }
  :     public virtual Stream InputStream { get; }
  : }
用於處理上傳文件的Action方法通常定義類型為HttpPostedFileBase及其列表的參數來表示上傳的文件針對HttpPostedFileBase參數的Model綁定選用的數據就來源於表示當前請求的HttpRequestBase的Files屬性而具體參數值的提供最終通過具有如下定義的HttpFileCollectionValueProvider來實現
   : public sealed class HttpFileCollectionValueProvider : DictionaryValueProvider<HttpPostedFileBase[]>
   : {   
   :     public HttpFileCollectionValueProvider(ControllerContext controllerContext);
   : }


如上面的代碼所示HttpFileCollectionValueProvider繼承自DictionaryValueProvider<TValue>泛型參數TValue的類型為HttpPostedFileBase數組這是因為在同一個表單中可以定義多個同名的文件輸入元素所以在以文件元素名稱作為Key的字典中字典元素的值自然就是一個HttpPostedFileBase的列表
為了讓讀者對HttpFileCollectionValueProvider采用的針對上傳文件的值對象提供機制具有一個深刻的認識我們來進行一個簡單的實例演示在通過Visual Studio的ASPNET MVC項目模板創建的空Web應用中創建一個具有如下定義的HomeController該Controller類型中定義了兩個Action方法默認的Index方法會將默認的View呈現出來DisplayPostedFiles方法則通過創建的HttpFileCollectionValueProvider對象將上傳文件的文件名稱呈現出來
   : public class HomeController : Controller
   : {
   :     public ActionResult Index()
   :     {
   :         return View();
   :     }
   :     [HttpPost]
   :     public void DisplayPostedFiles()
   :     {
  :         HttpFileCollectionValueProvider valueProvider = new HttpFileCollectionValueProvider(ControllerContext);
  :         IEnumerable<HttpPostedFileBase> foo = (IEnumerable<HttpPostedFileBase>)valueProviderGetValue(foo)ConvertTo(typeof(IEnumerable<HttpPostedFileBase>));
  :         IEnumerable<HttpPostedFileBase> bar =  (IEnumerable<HttpPostedFileBase>)valueProviderGetValue(bar)ConvertTo(typeof(IEnumerable<HttpPostedFileBase>));
 
  :         ResponseWrite(foo<br/>);
  :         foreach (var file in foo)
  :         {
  :             ResponseWrite(fileFileName + <br/>);
  :         }
 
  :         ResponseWrite(<br/>bar<br/>);
  :         foreach (var file in bar)
  :         {
  :             ResponseWrite(fileFileName + <br/>);
  :         }           
  :     }
  : }
在DisplayPostedFiles方法中我們針對當前Controller上下文創建HttpFileCollectionValueProvider對象然後分別將字符foobar作為Key得到兩個HttpPostedFileBase對象列表並將它們的文件名打印出來下面的代碼表示Action方法Index對應的View在一個針對Action方法DisplayPostedFiles的表單中我們定義了三個文件輸入元素其中前兩個名稱為foobar
   : @{
   :    using(HtmlBeginForm(DisplayPostedFilesHome
   :        FormMethodPostnew {enctype=multipart/formdata}))
   :    {
   :        <ul>
   :         <li>File : <input type=file name=foo/></li>
   :         <li>File : <input type=file  name=foo/></li>
   :         <li>File : <input type=file  name=bar/></li>
   :        </ul> 
  :        <input type=submit value=提交 />      
  :     }
  : }
當我們運行該程序的時候浏覽器上會出現一個包含三個文件輸入元素和提交按鈕的頁面然後我們從本地選擇任意三個文件(比如texttxttexttxt和texttxt)並點擊提交按鈕界面上會出現如下所示的輸出結果
   : foo
   : texttxt
   : texttxt
  
   : bar
   : texttxt

  ChildActionValueProvider
子Action和普通意義上的Action的不同之處在於它不能用於響應來自客戶端的請求而在某個View中被調用以生成某個部分的HTMLView中針對某個子Action方法的調用通過如下所示的HtmlHelper的擴展方法Action來實現
   : public static class ChildActionExtensions
   : {
   :     //其他成員
   :     public static MvcHtmlString Action(this HtmlHelper htmlHelper string actionName);
   :     public static MvcHtmlString Action(this HtmlHelper htmlHelper string actionName object routeValues);
   :     public static MvcHtmlString Action(this HtmlHelper htmlHelper string actionName string controllerName);
   :     public static MvcHtmlString Action(this HtmlHelper htmlHelper string actionName RouteValueDictionary routeValues);
   :     public static MvcHtmlString Action(this HtmlHelper htmlHelper string actionName string controllerName object routeValues);
   :     public static MvcHtmlString Action(this HtmlHelper htmlHelper string actionName string controllerName RouteValueDictionary routeValues); 
  : }


顧名思義ChildActionValueProvider專門服務於針對子Action方法參數的Model綁定如下面的代碼片斷所示ChildActionValueProvider依然是DictionaryValueProvider<TValue>的繼承者不過這裡的泛型參數類型Object那麼在作為數據源的字典中具體的Key和Value究竟是怎樣一個對象呢?
   : public sealed class ChildActionValueProvider : DictionaryValueProvider<object>
   : {
   :     public ChildActionValueProvider(ControllerContext controllerContext);
   :     public override ValueProviderResult GetValue(string key);
   : }
當我們創建針對指定的Controller上下文創建一個ChildActionValueProvider對象時會獲取描述針對該上下文路由信息的RouteData對象並將其Values屬性表示的RouteValueDictionary對象作為其數據源這可以從如下所示的ChildActionValueProvider的構造函數定義看出來
   : public sealed class ChildActionValueProvider : DictionaryValueProvider<object>
   : {
   :     //其他成員  
   :     public ChildActionValueProvider(ControllerContext controllerContext) :  base(controllerContextRouteDataValues CultureInfoInvariantCulture)
   :     {
   :     }
   : }
但是ChildActionValueProvider的GetValue方法獲取的值卻並不是簡單地來源於構造時針對當前上下文的路由信息不然ChildActionValueProvider就和RouteDataValueProvider沒有什麼分別了實際上ChildActionValueProvider的GetValue方法獲取的值來源於調用HtmHelper的擴展方法Action時通過參數routeValues指定的RouteValueDictionary
現在我們來簡單介紹一下定義在ChildActionValueProvider的GetValue方法中的對象值的提供機制如下面的代碼片斷所示ChildActionValueProvider具有一個字符串類型的靜態字段_childActionValuesKey當該類型第一次被加載時該字段被初始化成一個GUID
   : public sealed class ChildActionValueProvider : DictionaryValueProvider<object>
   : {
   :     //其他成員
   :     private static string _childActionValuesKey = GuidNewGuid()ToString();
   : }
在某個View中通過HtmlHelper的擴展方法Action執行子Action方法時如果通過參數routeValues指定的RouteValueDictionary不為空會基於這個對象創建一個DictionaryValueProvider<TValue>對象然後將這個對象添加到通過routeValues表示的原始的RouteValueDictionary對象中對應的Key就是ChildActionValueProvider的靜態屬性_childActionValuesKey表示的GUID
這個RouteValueDictionary被進一步封裝成表示請求上下文的RequestContext對象目標子Action所在的Controller會在該請求上下文中被激活而在Controller激活過程中表示Controller上下文的ControllerContext被創建出來毫無疑問它包含了之前創建的RouteValueDictionary對象而我們針對當前Controller上下文創建ChildActionValueProvider的時候指定的作為數據源的RouteValueDictionary對象就是這麼一個對象
   : @HtmlAction(XxxChildAction new {Foo= Bar = Baz=})
舉個例子假設我們在某個View中如果如下的方式調用當前Controller的子Action方法 XxxChildAction並指定相應的路由數據(FooBar和Baz)最終作為ChildActionValueProvider數據源的Dictionary<stringobject>對象結構如下圖所示

  

 
當調用ChildActionValueProvider的GetValue方法獲取指定Key的值時實際上並不會直接根據指定的Key去獲取對應的值而是根據通過其靜態字段_childActionValuesKey值去獲取對應的DictionaryValueProvider<object>對象然後再調用該對象的GetValue根據指定的Key去獲得相應的值


實例演示ChildActionValueProvider的值提供機制
為了印證上面介紹的關於ChildActionValueProvider的值提供機制我們來演示一個簡單的實例在進行演示之前有一個點需要作一下簡單說明對於DictionaryValueProvider<TValue>對象來說最終作為其數據源的通過私有字段_values表示的一個Dictionary<string ValueProviderResult對象當我們調用GetValue方法是只需要根據指定的Key從該字典對象中返回相應的ValueProviderResult即可
   : public class DictionaryValueProvider<TValue> : IEnumerableValueProvider IValueProvider
   : {
   :     //其他成員
   :     private readonly Dictionary<string ValueProviderResult> _values;
   : }
在通過Visual Studio的ASPNET MVC 項目模板創建的空Web應用中定義如下一個默認的HomeController默認的Action方法Index僅僅是將默認的View呈現出來而已並沒有特別之處在另一個Action方法DisplayRouteData中我們名稱分別為FooBar和Baz的三個路由數據篡改成abcijkzyz然後根據當前Controller上下文創建一個ChildActionValueProvider對象並通過反射的方式獲取通過它的私有字段_values表示的Dictionary<string ValueProviderResult對象
   : public class HomeController : Controller
   : {
   :     public ActionResult Index()
   :     {
   :         return View();
   :     }
  
   :     public ActionResult DisplayRouteData()
   :     {
  :         ControllerContextRouteDataValues[Foo] = abc;
  :         ControllerContextRouteDataValues[Bar] = ijk;
  :         ControllerContextRouteDataValues[Baz] = xyz;
 
  :         StringBuilder sb = new StringBuilder();
  :         ChildActionValueProvider valueProvider = new ChildActionValueProvider(ControllerContext);
  :         FieldInfo valuesField = typeof(DictionaryValueProvider<object>)GetField(_values BindingFlagsInstance|BindingFlagsNonPublic);
  :         Dictionary<string ValueProviderResult> values = (Dictionary<string ValueProviderResult>)valuesFieldGetValue(valueProvider);
  :         foreach (string key in valuesKeys)
  :         {
  :             sbAppend(stringFormat({}: {}<br/> key values[key]RawValue));
  :             DictionaryValueProvider<object> innerValueProvider = values[key]RawValue as DictionaryValueProvider<object>;
  :             if (innerValueProvider == null)
  :             {
  :                 continue;
  :             }
  :             sbAppend(stringFormat(&nbsp;&nbsp;&nbsp;&nbsp;{}: {}<br/> Foo innerValueProviderGetValue(Foo)RawValue));
  :             sbAppend(stringFormat(&nbsp;&nbsp;&nbsp;&nbsp;{}: {}<br/> Bar innerValueProviderGetValue(Bar)RawValue));
  :             sbAppend(stringFormat(&nbsp;&nbsp;&nbsp;&nbsp;{}: {}<br/> Baz innerValueProviderGetValue(Baz)RawValue));
  :         }
 
  :         sbAppend(<br/>ChildActionValueProviderGetValue()<br/>);
  :         sbAppend(stringFormat({}: {}<br/> Foo valueProviderGetValue(Foo)RawValue));
  :         sbAppend(stringFormat({}: {}<br/> Bar valueProviderGetValue(Bar)RawValue));
  :         sbAppend(stringFormat({}: {}<br/> Baz valueProviderGetValue(Baz)RawValue));           
 
  :         return Content(sbToString());
  :     }
  : }
我們創建一個StringBuilder對象並將用於輸出獲取到的Dictionary<string ValueProviderResult>對象的Key和Value的HTML添加其中在進行遍歷過程中如果ValueProviderResult對象的RawValue屬性是一個DictionaryValueProvider<object>對象則調用其GetValue方法得到Key分別為FooBar和Baz的值相應的輸出的HTML一並添加到StringBuilder中
在程序的最後我們直接調用ChildActionValueProvider的GetValue方法獲取針對FooBar和Baz作為Key的值並將輸出Key和Value的HTML添加到StringBuilder中最終針對生成的HTML字符串返回一個ContentResult對象如下所示的代碼反映Action方法Index對應的View的定義在這裡我們直接調用HtmlHelper的擴展方法Action執行定義在HomeController的Action方法DisplayRouteData並指定了相應的路由數據(FooBar和Baz)
   : @HtmlAction(DisplayRouteData new { Foo = Bar = Baz = })
運行我們的程序會在浏覽器中得到如下的輸出結果我們可以從中看到針對於Controller和Action名稱的路由數據和調用HtmlHelper擴展方法Action指定的數據數據均在作為ChildActionValueProvider數據源的字典對象中除此之外還具有一個DictionaryValueProvider<object>對象對應的Key是一個GUID這正是我們上面介紹的針對在HtmlHelper擴展方法Action中指定的路由數據創建的DictionaryValueProvider<object>對象而調用GetValue方法獲取到的值最終是通過它提供的
   : Foo: abc
   : Bar: ijk
   : Baz: xyz
   : controller: Home
   : action: DisplayRouteData
   : fdfbababbba:
   :     SystemWebMvcDictionaryValueProvider`[SystemObject]
   :     Foo:
   :     Bar:
  :     Baz:
 
  : ChildActionValueProviderGetValue()
  : Foo:
  : Bar:
  : Baz:

  ValueProviderCollection
類型ValueProviderCollection不僅僅表示一個ValueProvider對象的集合還作為一個單純的ValueProvider來使用如下面的代碼片斷所示ValueProviderCollection不僅僅繼承自Collection<IValueProvider>還同時實現了IValueProviderIEnumerableValueProvider和IUnvalidatedValueProvider三個接口
   : public class ValueProviderCollection : Collection<IValueProvider> IUnvalidatedValueProvider IEnumerableValueProvider IValueProvider
   : {
   :     public ValueProviderCollection();
   :     public ValueProviderCollection(IList<IValueProvider> list);
  
   :     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);  
  : }
對於兩個實現值提供機制的GetValue方法重載來說ValueProviderCollection會遍歷集合直到找到一個GetValue方法返回值不為Null的ValueProvider而該返回值就是該方法的返回值如果所有ValueProvider的GetValue方法均返回Null則ValueProviderCollection的GetValue方法也為Null也就是說ValueProvider在集合中的先後次序決定了其使用優先級
如果有任何一個ValueProvider的ContainsPrefix方法返回True則ValueProviderCollection的ContainsPrefix也返回TrueGetKeysFromPrefix方法的邏輯與GetValue方法類似它會遍歷作為集合中實現了IEnumerableValueProvider接口的所有ValueProvider對象並將指定的前綴作為參數調用ContainsPrefix方法如果返回值為True則直接返回GetKeysFromPrefix方法的結果否則返回一個空的Dictionary<string string>對象


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