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

通过实例模拟ASP.NET MVC的Model绑定的机制:集合+字典

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

在本系列的前面两篇文章(《简单类型+复杂类型》、《数组》)我们通过创建的实例程序模拟了ASP.NET MVC默认使用的DefaultModelBinder对简单类型、复杂类型以及数组对象的Model绑定。现在我们按照相同的方式来分析基于集合和字典类型的Model绑定是如何实现的。[源代码从这里下载][本文已经同步到《How ASP.NET MVC Works?》中]

这里的集合指的是除数组和字典之外的所有实现IEnumerable<T>接口的类型。和基于数组的Model绑定类似,ValueProvider可以将多个同名的数据项作为集合的元素,基于索引(基零整数和字符串)的数据项命名方式同样适用。我们对自定义的DefaultModelBinder作了如下的完善使之支持集合类型的Model绑定。

class DefaultModelBinder
   2: {
//其他成员
string prefix)
   5:     {
this.ValueProvider.ContainsPrefix(prefix))
   7:         {
null;
   9:         }
null, parameterType);
if (!modelMetadata.IsComplexType)
  12:         {
this.ValueProvider.GetValue(prefix).ConvertTo(parameterType);
  14:         }
if (parameterType.IsArray)
  16:         {
return BindArrayModel(parameterType, prefix);
  18:         }
object model = CreateModel(parameterType);
typeof(IEnumerable<>));
null != enumerableType)
  22:         {
return BindCollectionModel(prefix, model, enumerableType);
  24:         }
in TypeDescriptor.GetProperties(parameterType))
  26:         {                
 + property.Name;
  28:             property.SetValue(model, BindModel(property.PropertyType, key));
  29:         }
return model;
  31:     }
  32:  
object model, Type enumerableType)
  34:     {
object>();
bool numericIndex;
out numericIndex);
  38:         Type elementType = enumerableType.GetGenericArguments()[0];
  39:  
this.ValueProvider.ContainsPrefix(prefix))
  41:         {
as IEnumerable;
null != enumerable)
  44:             {
in enumerable)
  46:                 {
value);
  48:                 }
  49:             }
  50:         }      
in indexes)
  52:         {
;
this.ValueProvider.ContainsPrefix(indexPrefix) && numericIndex)
  55:             {
break;
  57:             }
  58:             list.Add(BindModel(elementType, indexPrefix));
  59:         }
if (list.Count == 0)
  61:         {
null;
  63:         }
  64:         ReplaceHelper.ReplaceCollection(elementType, model, list);
return model;
  66:     }
  67:     
private Type ExtractGenericInterface(Type queryType, Type interfaceType)
  69:     {
bool> predicate = t => t.IsGenericType && (t.GetGenericTypeDefinition() == interfaceType);
if (!predicate(queryType))
  72:         {
return queryType.GetInterfaces().FirstOrDefault<Type>(predicate);
  74:         }
return queryType;
  76:     }
  77: }

如上面的代码片断所示,在BindModel方法中我们通过调用ExtractGenericInterface判断目标类型是否实现了IEnumerable<T>接口,如果实现了该接口则提取泛型元素类型。针对集合的Model绑定实现在方法BindCollectionModel中,我们按照数组绑定的方式得的针对目标集合对象的所有元素对象,并将其添加到一个List<object>对象中,然后调用ReplaceHelper 的静态方法ReplaceCollection将该列表中的元素拷贝到预先创建的Model对象中。定义在ReplaceHelper的静态方法ReplaceCollection定义如下:

class ReplaceHelper
   2: {
, BindingFlags.Static |BindingFlags.NonPublic);
   4:  
object newContents)
   6:     {
object[] { collection, newContents });
   8:     } 
void ReplaceCollectionImpl<T>(ICollection<T> collection, IEnumerable newContents)
  10:     {
  11:         collection.Clear();
null)
  13:         {
in newContents)
  15:             {
default(T);
  17:                 collection.Add(item);
  18:             }
  19:         }
  20:     }
  21: }

为了让演示针对集合类型的Model绑定,我们对实例中的HomeController作了如下的修改。Action方法的参数类型替换成IEnumerable<Contact>,该集合中的每个Contact的信息在该方法中被呈现出来。通过GetValueProvider提供的NameValueCollectionValueProvider采用基零整数索引的方式定义数据项。

class HomeController : Controller
   2: {
private IValueProvider GetValueProvider()
   4:     {
new NameValueCollection();
);
);
);
   9:  
);
);
);
  13:  
new NameValueCollectionValueProvider(requestData, CultureInfo.InvariantCulture);
  15:     }
  16:          
void Action(IEnumerable<Contact> contacts)
  18:     {
in contacts)
  20:         {
, contact.Name));
, contact.PhoneNo));
,contact.EmailAddress));
  24:         }
  25:     }
  26: }

该程序被执行之后,在浏览器上依然会呈现出如下所示的我们希望的数据,这充分证明了我们自定义的DefaultModelBinder具有针对集合的绑定能力。

   1: Name: Foo
   2: PhoneNo: 123456789
   3: EmailAddress: [email protected]
   4:  
   5: Name: Bar
   6: PhoneNo: 987654321
   7: EmailAddress: [email protected]

二、 字典

这里的字典指的是实现了接口IDictionary<TKey,TValue>的类型。在Model绑定过程中基于字典类型的数据映射很好理解,首先,字典是一个KeyValuePair<TKey,TValue>对象的集合,所以在字典元素这一级可以采用基于索引的匹配机制;其次,KeyValuePair<TKey,TValue>是一个复杂类型,可以按照属性名称(Key和Value)进行匹配。比如说作为某个ValueProvider数据源的NameValueCollection具有如下的结构,它可以映射为一个IDictionary<string, Contact>对象(Contact对象作为Value,其Name属性作为Key)。

   1: [0].Key               : Foo
   2: [0].Value.Name        : Foo
   3: [0].Value.EmailAddress: [email protected]
   4: [0].Value.PhoneNo     : 123456789
   5:  
   6: [1].Key               : Bar
   7: [1].Value.Name        : Bar
   8: [1].Value.EmailAddress: [email protected]
   9: [1].Value.PhoneNo     : 987654321

现在我们对用于模拟默认Model绑定的自定义DefaultModelBinder作最后的完善,使之支持针对字典类型的Model绑定。如下面的代码片断所示,在通过调用CreateModel创建Model对象之后,我们调用ExtractGenericInterface方法判断目标类型是否是一个字典,如果是则返回具体的字典类型,然后调用BindDictionaryModel方法实施针对字典类型的Model绑定。

class DefaultModelBinder
   2: {
//其他成员
string prefix)
   5:     {
this.ValueProvider.ContainsPrefix(prefix))
   7:         {
null;
   9:         }
  10:  
null, parameterType);
if (!modelMetadata.IsComplexType)
  13:         {
this.ValueProvider.GetValue(prefix).ConvertTo(parameterType);
  15:         }
if (parameterType.IsArray)
  17:         {
return BindArrayModel(parameterType, prefix);
  19:         }
object model = CreateModel(parameterType);
typeof(IDictionary<,>));
null != dictionaryType)
  23:         {
return BindDictionaryModel(prefix, model, dictionaryType);
  25:         }
  26:  
typeof(IEnumerable<>));
null != enumerableType)
  29:         {
 
                       
                    
                    

鲜花

握手

雷人

路过

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

请发表评论

全部评论

专题导读
上一篇:
asp.net伪静态实现(UrlRewritingNet)发布时间:2022-07-10
下一篇:
asp.net判断字符串是否是数字的,int的,整型的发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

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

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

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