好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

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

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

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

一、DictionaryValueProvider<TValue>

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

    1:   public   class  DictionaryValueProvider<TValue> : IEnumerableValueProvider, IValueProvider
    2:  {
    3:       public  DictionaryValueProvider(IDictionary< string , TValue> dictionary, CultureInfo culture);
    4:       public   virtual   bool  ContainsPrefix( string  prefix);
    5:       public   virtual  IDictionary< string ,  string > GetKeysFromPrefix( string  prefix);
    6:       public   virtual  ValueProviderResult GetValue( string  key);
    7:  }


二、RouteDataValueProvider

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

    1:   public   sealed   class  RouteDataValueProvider : DictionaryValueProvider< object >
    2:  {
    3:       public  RouteDataValueProvider(ControllerContext controllerContext) : 
    4:           base (controllerContext.RouteData.Values, CultureInfo.InvariantCulture)
    5:      {
    6:      }
    7:  }


三、HttpFileCollectionValueProvider

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

    1:   public   abstract   class  HttpRequestBase
    2:  {   
    3:       public   virtual  HttpFileCollectionBase Files { get; }
    4:  }
    5:   public   abstract   class  HttpFileCollectionBase : NameObjectCollectionBase, ICollection, IEnumerable
    6:  {   
    7:       public   virtual   string [] AllKeys { get; }
    8:       public   override   int  Count { get; }
    9:       public   virtual  HttpPostedFileBase  this [ int  index] { get; }
   10:       public   virtual  HttpPostedFileBase  this [ string  name] { get; }
   11:  }
   12:   public   abstract   class  HttpPostedFileBase
   13:  {
   14:       public   virtual   void  SaveAs( string  filename);
   15:   
   16:       public   virtual   int  ContentLength { get; }
   17:       public   virtual   string  ContentType { get; }
   18:       public   virtual   string  FileName { get; }
   19:       public   virtual  Stream InputStream { get; }
   20:  }

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

    1:   public   sealed   class  HttpFileCollectionValueProvider : DictionaryValueProvider<HttpPostedFileBase[]>
    2:  {    
    3:       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对象将上传文件的文件名称呈现出来。

    1:   public   class  HomeController : Controller
    2:  {
    3:       public  ActionResult Index()
    4:      {
    5:           return  View();
    6:      }
    7:      [HttpPost]
    8:       public   void  DisplayPostedFiles()
    9:      {
   10:          HttpFileCollectionValueProvider valueProvider =  new  HttpFileCollectionValueProvider(ControllerContext);
   11:          IEnumerable<HttpPostedFileBase> foo = (IEnumerable<HttpPostedFileBase>)valueProvider.GetValue( "foo" ).ConvertTo( typeof (IEnumerable<HttpPostedFileBase>));
   12:          IEnumerable<HttpPostedFileBase> bar =  (IEnumerable<HttpPostedFileBase>)valueProvider.GetValue( "bar" ).ConvertTo( typeof (IEnumerable<HttpPostedFileBase>));
   13:   
   14:          Response.Write( "foo<br/>" );
   15:           foreach  (var file  in  foo)
   16:          {
   17:              Response.Write(file.FileName +  "<br/>" );
   18:          }
   19:   
   20:          Response.Write( "<br/>bar<br/>" );
   21:           foreach  (var file  in  bar)
   22:          {
   23:              Response.Write(file.FileName +  "<br/>" );
   24:          }            
   25:      }
   26:  }

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

    1:  @{
    2:     using(Html.BeginForm("DisplayPostedFiles","Home", 
    3:         FormMethod.Post,new {enctype="multipart/form-data"}))
    4:     {
    5:          <  ul  > 
    6:           <  li  > File 1:  <  input   type  ="file"   name  ="foo"  /></  li  > 
    7:           <  li  > File 2:  <  input   type  ="file"    name  ="foo"  /></  li  > 
    8:           <  li  > File 3:  <  input   type  ="file"    name  ="bar"  /></  li  > 
    9:          </  ul  >   
   10:          <  input   type  ="submit"   value  ="提交"   />        
   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究竟是怎样一个对象呢?

    1:   public   sealed   class  ChildActionValueProvider : DictionaryValueProvider< object >
    2:  {
    3:       public  ChildActionValueProvider(ControllerContext controllerContext);
    4:       public   override  ValueProviderResult GetValue( string  key);
    5:  }

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

    1:   public   sealed   class  ChildActionValueProvider : DictionaryValueProvider< object >
    2:  {
    3:       //其他成员    
    4:       public  ChildActionValueProvider(ControllerContext controllerContext) :   base (controllerContext.RouteData.Values, CultureInfo.InvariantCulture)
    5:      {
    6:      }
    7:  }

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

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

    1:   public   sealed   class  ChildActionValueProvider : DictionaryValueProvider< object >
    2:  {
    3:       //其他成员 
    4:       private   static   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对象就是这么一个对象。

    1:  @Html.Action("XxxChildAction", new {Foo=123, Bar = 456, Baz=789})

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

    1:   public   class  DictionaryValueProvider<TValue> : IEnumerableValueProvider, IValueProvider
    2:  {
    3:       //其他成员 
    4:       private   readonly  Dictionary< 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对象。

    1:   public   class  HomeController : Controller
    2:  {
    3:       public  ActionResult Index()
    4:      {
    5:           return  View();
    6:      }
    7:   
    8:       public  ActionResult DisplayRouteData()
    9:      {
   10:          ControllerContext.RouteData.Values[ "Foo" ] =  "abc" ;
   11:          ControllerContext.RouteData.Values[ "Bar" ] =  "ijk" ;
   12:          ControllerContext.RouteData.Values[ "Baz" ] =  "xyz" ;
   13:   
   14:          StringBuilder sb =  new  StringBuilder();
   15:          ChildActionValueProvider valueProvider =  new  ChildActionValueProvider(ControllerContext);
   16:          FieldInfo valuesField =  typeof (DictionaryValueProvider< object >).GetField( "_values" , BindingFlags.Instance|BindingFlags.NonPublic);
   17:          Dictionary< string , ValueProviderResult> values = (Dictionary< string , ValueProviderResult>)valuesField.GetValue(valueProvider);
   18:           foreach  ( string  key  in  values.Keys)
   19:          {
   20:              sb.Append( string .Format( "{0}: {1}<br/>" , key, values[key].RawValue));
   21:              DictionaryValueProvider< object > innerValueProvider = values[key].RawValue  as  DictionaryValueProvider< object >;
   22:               if  (innerValueProvider ==  null )
   23:              {
   24:                   continue ;
   25:              }
   26:              sb.Append( string .Format( "&nbsp;&nbsp;&nbsp;&nbsp;{0}: {1}<br/>" ,  "Foo" , innerValueProvider.GetValue( "Foo" ).RawValue));
   27:              sb.Append( string .Format( "&nbsp;&nbsp;&nbsp;&nbsp;{0}: {1}<br/>" ,  "Bar" , innerValueProvider.GetValue( "Bar" ).RawValue));
   28:              sb.Append( string .Format( "&nbsp;&nbsp;&nbsp;&nbsp;{0}: {1}<br/>" ,  "Baz" , innerValueProvider.GetValue( "Baz" ).RawValue));
   29:          }
   30:   
   31:          sb.Append( "<br/>ChildActionValueProvider.GetValue()<br/>" );
   32:          sb.Append( string .Format( "{0}: {1}<br/>" ,  "Foo" , valueProvider.GetValue( "Foo" ).RawValue));
   33:          sb.Append( string .Format( "{0}: {1}<br/>" ,  "Bar" , valueProvider.GetValue( "Bar" ).RawValue));
   34:          sb.Append( string .Format( "{0}: {1}<br/>" ,  "Baz" , valueProvider.GetValue( "Baz" ).RawValue));            
   35:   
   36:           return  Content(sb.ToString());
   37:      }
   38:  }

我们创建一个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)。

    1:  @Html.Action("DisplayRouteData", new { Foo = 123, Bar = 456, Baz = 789 })

运行我们的程序会在浏览器中得到如下的输出结果。我们可以从中看到针对于Controller和Action名称的路由数据和调用HtmlHelper扩展方法Action指定的数据数据均在作为ChildActionValueProvider数据源的字典对象中。除此之外,还具有一个DictionaryValueProvider<object>对象,对应的Key是一个GUID,这正是我们上面介绍的针对在HtmlHelper扩展方法Action中指定的路由数据创建的DictionaryValueProvider<object>对象,而调用GetValue方法获取到的值最终是通过它提供的。

    1:  Foo: abc
    2:  Bar: ijk
    3:  Baz: xyz
    4:  controller: Home
    5:  action: DisplayRouteData
    6:  289594f6-dfba-45b9-8abb-158b4a582911: 
    7:      System.Web.Mvc.DictionaryValueProvider`1[System.Object]
    8:      Foo: 123
    9:      Bar: 456
   10:      Baz: 789
   11:   
   12:  ChildActionValueProvider.GetValue()
   13:  Foo: 123
   14:  Bar: 456
   15:  Baz: 789


六、ValueProviderCollection

类型 ValueProviderCollection 不仅仅表示一个ValueProvider对象的集合,还 作为一个单纯的ValueProvider来使用 。如下面的代码片断所示,ValueProviderCollection不仅仅继承自Collection<IValueProvider>,还同时实现了IValueProvider、IEnumerableValueProvider和IUnvalidatedValueProvider三个接口。

    1:   public   class  ValueProviderCollection : Collection<IValueProvider>, IUnvalidatedValueProvider, IEnumerableValueProvider, IValueProvider
    2:  {
    3:       public  ValueProviderCollection();
    4:       public  ValueProviderCollection(IList<IValueProvider> list);
    5:   
    6:       public   virtual   bool  ContainsPrefix( string  prefix);
    7:       public   virtual  IDictionary< string ,  string > GetKeysFromPrefix( string  prefix);  
    8:       public   virtual  ValueProviderResult GetValue( string  key);
    9:       public   virtual  ValueProviderResult GetValue( string  key,  bool  skipValidation);   
   10:  }

对于两个实现值提供机制的GetValue方法重载来说,ValueProviderCollection会遍历集合直到找到一个GetValue方法返回值不为Null的ValueProvider,而该返回值就是该方法的返回值。如果所有ValueProvider的GetValue方法均返回Null,则ValueProviderCollection的GetValue方法也为Null。也就是说, ValueProvider在集合中的先后次序决定了其使用优先级 。

如果有任何一个ValueProvider的ContainsPrefix方法返回True,则ValueProviderCollection的ContainsPrefix也返回True。GetKeysFromPrefix方法的逻辑与GetValue方法类似,它会遍历作为集合中实现了IEnumerableValueProvider接口的所有ValueProvider对象,并将指定的前缀作为参数调用ContainsPrefix方法,如果返回值为True,则直接返回GetKeysFromPrefix方法的结果;否则返回一个空的Dictionary<string, string>对象。

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

作者: Artech
出处: http://artech.cnblogs测试数据/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于ASP.NET MVC以ValueProvider为核心的值提供系统: DictionaryValu的详细内容...

  阅读:53次