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的ASP
NET 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>)valueProvider
GetValue(
foo
)
ConvertTo(typeof(IEnumerable<HttpPostedFileBase>));
: IEnumerable<HttpPostedFileBase> bar = (IEnumerable<HttpPostedFileBase>)valueProvider
GetValue(
bar
)
ConvertTo(typeof(IEnumerable<HttpPostedFileBase>));
:
: Response
Write(
foo<br/>
);
: foreach (var file in foo)
: {
: Response
Write(file
FileName +
<br/>
);
: }
:
: Response
Write(
<br/>bar<br/>
);
: foreach (var file in bar)
: {
: Response
Write(file
FileName +
<br/>
);
: }
: }
: }
在DisplayPostedFiles方法中
我們針對當前Controller上下文創建HttpFileCollectionValueProvider對象
然後分別將字符
foo
和
bar
作為Key得到兩個HttpPostedFileBase對象列表
並將它們的文件名打印出來
下面的代碼表示Action方法Index對應的View
在一個針對Action方法DisplayPostedFiles的表單中我們定義了三個文件輸入元素
其中前兩個名稱為
foo
和
bar
: @{
: using(Html
BeginForm(
DisplayPostedFiles
Home
: FormMethod
Post
new {enctype=
multipart/form
data
}))
: {
: <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=
提交
/>
: }
: }
當我們運行該程序的時候
浏覽器上會出現一個包含三個文件輸入元素和提交按鈕的頁面
然後我們從本地選擇任意三個文件(比如text
txt
text
txt和text
txt)並點擊
提交
按鈕
界面上會出現如下所示的輸出結果
: foo
: text
txt
: text
txt
:
: bar
: text
txt
四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(controllerContext
RouteData
Values
CultureInfo
InvariantCulture)
: {
: }
: }
但是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 = Guid
NewGuid()
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對象就是這麼一個對象
: @Html
Action(
XxxChildAction
new {Foo=
Bar =
Baz=
})
舉個例子
假設我們在某個View中如果如下的方式調用當前Controller的子Action方法 XxxChildAction
並指定相應的路由數據(Foo
Bar和Baz)
最終作為ChildActionValueProvider數據源的Dictionary<string
object>對象結構如下圖所示
當調用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的ASP
NET MVC 項目模板創建的空Web應用中定義如下一個默認的HomeController
默認的Action方法Index僅僅是將默認的View呈現出來而已
並沒有特別之處
在另一個Action方法DisplayRouteData中
我們名稱分別為Foo
Bar和Baz的三個路由數據篡改成
abc
ijk
和
zyz
然後根據當前Controller上下文創建一個ChildActionValueProvider對象
並通過反射的方式獲取通過它的私有字段_values表示的Dictionary<string
ValueProviderResult對象
: public class HomeController : Controller
: {
: public ActionResult Index()
: {
: return View();
: }
:
: public ActionResult DisplayRouteData()
: {
: ControllerContext
RouteData
Values[
Foo
] =
abc
;
: ControllerContext
RouteData
Values[
Bar
] =
ijk
;
: ControllerContext
RouteData
Values[
Baz
] =
xyz
;
:
: StringBuilder sb = new StringBuilder();
: ChildActionValueProvider valueProvider = new ChildActionValueProvider(ControllerContext);
: FieldInfo valuesField = typeof(DictionaryValueProvider<object>)
GetField(
_values
BindingFlags
Instance|BindingFlags
NonPublic);
: Dictionary<string
ValueProviderResult> values = (Dictionary<string
ValueProviderResult>)valuesField
GetValue(valueProvider);
: foreach (string key in values
Keys)
: {
: sb
Append(string
Format(
{
}: {
}<br/>
key
values[key]
RawValue));
: DictionaryValueProvider<object> innerValueProvider = values[key]
RawValue as DictionaryValueProvider<object>;
: if (innerValueProvider == null)
: {
: continue;
: }
: sb
Append(string
Format(
{
}: {
}<br/>
Foo
innerValueProvider
GetValue(
Foo
)
RawValue));
: sb
Append(string
Format(
{
}: {
}<br/>
Bar
innerValueProvider
GetValue(
Bar
)
RawValue));
: sb
Append(string
Format(
{
}: {
}<br/>
Baz
innerValueProvider
GetValue(
Baz
)
RawValue));
: }
:
: sb
Append(
<br/>ChildActionValueProvider
GetValue()<br/>
);
: sb
Append(string
Format(
{
}: {
}<br/>
Foo
valueProvider
GetValue(
Foo
)
RawValue));
: sb
Append(string
Format(
{
}: {
}<br/>
Bar
valueProvider
GetValue(
Bar
)
RawValue));
: sb
Append(string
Format(
{
}: {
}<br/>
Baz
valueProvider
GetValue(
Baz
)
RawValue));
:
: return Content(sb
ToString());
: }
: }
我們創建一個StringBuilder對象
並將用於輸出獲取到的Dictionary<string
ValueProviderResult>對象的Key和Value的HTML添加其中
在進行遍歷過程中
如果ValueProviderResult對象的RawValue屬性是一個DictionaryValueProvider<object>對象
則調用其GetValue方法得到Key分別為Foo
Bar和Baz的值
相應的輸出的HTML一並添加到StringBuilder中
在程序的最後
我們直接調用ChildActionValueProvider的GetValue方法獲取針對Foo
Bar和Baz作為Key的值
並將輸出Key和Value的HTML添加到StringBuilder中
最終針對生成的HTML字符串返回一個ContentResult對象
如下所示的代碼反映Action方法Index對應的View的定義
在這裡我們直接調用HtmlHelper的擴展方法Action執行定義在HomeController的Action方法DisplayRouteData
並指定了相應的路由數據(Foo
Bar和Baz)
: @Html
Action(
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
:
f
dfba
b
abb
b
a
:
: System
Web
Mvc
DictionaryValueProvider`
[System
Object]
: Foo:
: Bar:
: Baz:
:
: ChildActionValueProvider
GetValue()
: 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