• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

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

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

NameValueCollectionValueProvider采用一个NameValueCollection作为数据源,DictionnaryValueProvider的数据源类型自然就是一个Dictionnary。NameValueCollection和Dictionnary都是一个键值对的集合,它们之间的不同之处在NameValueCollection运行元素具有相同的Key,Dictionnary却要求元素的Key具有唯一性。[本文已经同步到《How ASP.NET MVC Works?》中]

目录
一、DictionaryValueProvider<TValue>
二、RouteDataValueProvider
三、HttpFileCollectionValueProvider
四、ChildActionValueProvider
五、实例演示:ChildActionValueProvider的值提供机制
六、ValueProviderCollection

DictionnaryValueProvider的类型全名为System.Web.Mvc.DictionaryValueProvider<TValue>,如下面的代码片断所示,DictionaryValueProvider<TValue>实现了IEnumerableValueProvider和IValueProvider接口,构造函数接受一个IDictionary<string, TValue>对象,该对象表示作为数据源的字典。定义在DictionaryValueProvider<TValue>中所有方法的逻辑与定义在NameValueCollectionValueProvider中的同名方法并没有本质区别。

class DictionaryValueProvider<TValue> : IEnumerableValueProvider, IValueProvider
   2: {
string, TValue> dictionary, CultureInfo culture);
string prefix);
string prefix);
string key);
   7: }

二、RouteDataValueProvider

将当前路由数据作为数据源的RouteDataValueProvider继承自DictionaryValueProvider<TValue>。如下面的代码片断所示,基于当前Controller上下文构建的RouteDataValueProvider直接将表示当前路由数据的RouteData对象的Values属性(这是一个RouteValueDictionary对象)作为数据来源。

object>
   2: {
public RouteDataValueProvider(ControllerContext controllerContext) : 
base(controllerContext.RouteData.Values, CultureInfo.InvariantCulture)
   5:     {
   6:     }
   7: }

三、HttpFileCollectionValueProvider

我们可以通过类型为file的输入元素进行文件的上传,在表示HTTP请求的HttpRequestBase对象中,上传文件通过只读属性Files表示。从下面的代码片断所示,该属性类型为HttpFileCollectionBase,是一个元素类型为HttpPostedFileBase的集合。

class HttpRequestBase
   2: {   
virtual HttpFileCollectionBase Files { get; }
   4: }
class HttpFileCollectionBase : NameObjectCollectionBase, ICollection, IEnumerable
   6: {   
string[] AllKeys { get; }
int Count { get; }
int index] { get; }
string name] { get; }
  11: }
class HttpPostedFileBase
  13: {
string filename);
  15:  
int ContentLength { get; }
string ContentType { get; }
string FileName { get; }
virtual Stream InputStream { get; }
  20: }

用于处理上传文件的Action方法通常定义类型为HttpPostedFileBase及其列表的参数来表示上传的文件,针对HttpPostedFileBase参数的Model绑定选用的数据就来源于表示当前请求的HttpRequestBase的Files属性,而具体参数值的提供最终通过具有如下定义的HttpFileCollectionValueProvider来实现。

class HttpFileCollectionValueProvider : DictionaryValueProvider<HttpPostedFileBase[]>
   2: {    
public HttpFileCollectionValueProvider(ControllerContext controllerContext);
   4: }

如上面的代码所示,HttpFileCollectionValueProvider继承自DictionaryValueProvider<TValue>,泛型参数TValue的类型为HttpPostedFileBase数组,这是因为在同一个表单中可以定义多个同名的文件输入元素,所以在以文件元素名称作为Key的字典中,字典元素的值自然就是一个HttpPostedFileBase的列表。

为了让读者对HttpFileCollectionValueProvider采用的针对上传文件的值对象提供机制具有一个深刻的认识,我们来进行一个简单的实例演示。在通过Visual Studio的ASP.NET MVC项目模板创建的空Web应用中创建一个具有如下定义的HomeController。该Controller类型中定义了两个Action方法,默认的Index方法会将默认的View呈现出来,DisplayPostedFiles方法则通过创建的HttpFileCollectionValueProvider对象将上传文件的文件名称呈现出来。

class HomeController : Controller
   2: {
public ActionResult Index()
   4:     {
return View();
   6:     }
   7:     [HttpPost]
void DisplayPostedFiles()
   9:     {
new HttpFileCollectionValueProvider(ControllerContext);
typeof(IEnumerable<HttpPostedFileBase>));
typeof(IEnumerable<HttpPostedFileBase>));
  13:  
);
in foo)
  16:         {
);
  18:         }
  19:  
);
in bar)
  22:         {
);
  24:         }            
  25:     }
  26: }

在DisplayPostedFiles方法中,我们针对当前Controller上下文创建HttpFileCollectionValueProvider对象,然后分别将字符“foo”和“bar”作为Key得到两个HttpPostedFileBase对象列表,并将它们的文件名打印出来。下面的代码表示Action方法Index对应的View。在一个针对Action方法DisplayPostedFiles的表单中我们定义了三个文件输入元素,其中前两个名称为“foo”和“bar”。

   1: @{



    



    
   4:    {
>
>
>
>
>  
/>       
  11:     }
  12: }

当我们运行该程序的时候,浏览器上会出现一个包含三个文件输入元素和提交按钮的页面。然后我们从本地选择任意三个文件(比如text1.txt、text2.txt和text2.txt)并点击“提交”按钮,界面上会出现如下所示的输出结果。

   1: foo
   2: text1.txt
   3: text2.txt
   4:  
   5: bar
   6: text3.txt

四、ChildActionValueProvider

子Action和普通意义上的Action的不同之处在于它不能用于响应来自客户端的请求,而在某个View中被调用以生成某个部分的HTML。View中针对某个子Action方法的调用通过如下所示的HtmlHelper的扩展方法Action来实现。

   1: public static class ChildActionExtensions
   2: {
   3:     //其他成员
   4:     public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName);
   5:     public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, object routeValues);
   6:     public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, string controllerName);
   7:     public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, RouteValueDictionary routeValues);
   8:     public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, string controllerName, object routeValues);
   9:     public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues);  
  10: }

顾名思义,ChildActionValueProvider专门服务于针对子Action方法参数的Model绑定。如下面的代码片断所示,ChildActionValueProvider依然是DictionaryValueProvider<TValue>的继承者,不过这里的泛型参数类型Object。那么在作为数据源的字典中,具体的Key和Value究竟是怎样一个对象呢?

object>
   2: {
public ChildActionValueProvider(ControllerContext controllerContext);
string key);
   5: }

当我们创建针对指定的Controller上下文创建一个ChildActionValueProvider对象时,会获取描述针对该上下文路由信息的RouteData对象,并将其Values属性表示的RouteValueDictionary对象作为其数据源,这可以从如下所示的ChildActionValueProvider的构造函数定义看出来。

object>
   2: {
//其他成员   
base(controllerContext.RouteData.Values, CultureInfo.InvariantCulture)
   5:     {
   6:     }
   7: }

但是ChildActionValueProvider的GetValue方法获取的值却并不是简单地来源于构造时针对当前上下文的路由信息,不然ChildActionValueProvider就和RouteDataValueProvider没有什么分别了。实际上,ChildActionValueProvider的GetValue方法获取的值来源于调用HtmHelper的扩展方法Action时通过参数routeValues指定的RouteValueDictionary。

现在我们来简单介绍一下定义在ChildActionValueProvider的GetValue方法中的对象值的提供机制。如下面的代码片断所示,ChildActionValueProvider具有一个字符串类型的静态字段_childActionValuesKey。当该类型第一次被加载时,该字段被初始化成一个GUID。

object>
   2: {
//其他成员
string _childActionValuesKey = Guid.NewGuid().ToString();
   5: }

在某个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对象就是这么一个对象。


举个例子,假设我们在某个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即可。

class DictionaryValueProvider<TValue> : IEnumerableValueProvider, IValueProvider
   2: {
//其他成员
string, ValueProviderResult> _values;
   5: }

在通过Visual Studio的ASP.NET MVC 项目模板创建的空Web应用中定义如下一个默认的HomeController。默认的Action方法Index仅仅是将默认的View呈现出来而已,并没有特别之处。在另一个Action方法DisplayRouteData中,我们名称分别为Foo、Bar和Baz的三个路由数据篡改成“abc”、“ijk”和“zyz”。然后根据当前Controller上下文创建一个ChildActionValueProvider对象,并通过反射的方式获取通过它的私有字段_values表示的Dictionary<string, ValueProviderResult对象。

class HomeController : Controller
   2: {
public ActionResult Index()
   4:     {
return View();
   6:     }
   7:  
public ActionResult DisplayRouteData()
   9:     {
;
;
;
  13:  
new StringBuilder();
new ChildActionValueProvider(ControllerContext);
, BindingFlags.Instance|BindingFlags.NonPublic);
string, ValueProviderResult>)valuesField.GetValue(valueProvider);
in values.Keys)
  19:         {
, key, values[key].RawValue));
object>;
null)
  23:             {
continue;
  25:             }
).RawValue));
).RawValue));
).RawValue));
  29:         }
  30:  
);
).RawValue));

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap