在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
在本系列的前面两篇文章(《简单类型+复杂类型》、《数组》)我们通过创建的实例程序模拟了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: {
全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论