在本系列的前面两篇文章(《简单类型+复杂类型》、《数组》)我们通过创建的实例程序模拟了ASP.NET MVC默认使用的DefaultModelBinder对简单类型、复杂类型以及数组对象的Model绑定。现在我们按照相同的方式来分析基于集合和字典类型的Model绑定是如何实现的。[源代码从这里下载]
一、集合
这里的集合指的是除数组和字典之外的所有实现IEnumerable<T>接口的类型。和基于数组的Model绑定类似,ValueProvider可以将多个同名的数据项作为集合的元素,基于索引(基零整数和字符串)的数据项命名方式同样适用。我们对自定义的DefaultModelBinder作了如下的完善使之支持集合类型的Model绑定。
1: public class DefaultModelBinder 2: { 3: //其他成员 4: public object BindModel(Type parameterType, string prefix) 5: { 6: if (!this.ValueProvider.ContainsPrefix(prefix)) 7: { 8: return null; 9: } 10: ModelMetadata modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => null, parameterType); 11: if (!modelMetadata.IsComplexType) 12: { 13: return this.ValueProvider.GetValue(prefix).ConvertTo(parameterType); 14: } 15: if (parameterType.IsArray) 16: { 17: return BindArrayModel(parameterType, prefix); 18: } 19: object model = CreateModel(parameterType); 20: Type enumerableType = ExtractGenericInterface(parameterType, typeof(IEnumerable<>)); 21: if (null != enumerableType) 22: { 23: return BindCollectionModel(prefix, model, enumerableType); 24: } 25: foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(parameterType)) 26: { 27: string key = prefix == "" ? property.Name : prefix + "." + property.Name; 28: property.SetValue(model, BindModel(property.PropertyType, key)); 29: } 30: return model; 31: } 32: 33: private object BindCollectionModel(string prefix, object model, Type enumerableType) 34: { 35: List<object> list = new List<object>(); 36: bool numericIndex; 37: IEnumerable<string> indexes = GetIndexes(prefix, out numericIndex); 38: Type elementType = enumerableType.GetGenericArguments()[0]; 39: 40: if (!string.IsNullOrEmpty(prefix) && this.ValueProvider.ContainsPrefix(prefix)) 41: { 42: IEnumerable enumerable = this.ValueProvider.GetValue(prefix).ConvertTo(enumerableType) as IEnumerable; 43: if (null != enumerable) 44: { 45: foreach (var value in enumerable) 46: { 47: list.Add(value); 48: } 49: } 50: } 51: foreach (var index in indexes) 52: { 53: string indexPrefix = prefix + "[" + index + "]"; 54: if (!this.ValueProvider.ContainsPrefix(indexPrefix) && numericIndex) 55: { 56: break; 57: } 58: list.Add(BindModel(elementType, indexPrefix)); 59: } 60: if (list.Count == 0) 61: { 62: return null; 63: } 64: ReplaceHelper.ReplaceCollection(elementType, model, list); 65: return model; 66: } 67: 68: private Type ExtractGenericInterface(Type queryType, Type interfaceType) 69: { 70: Func<Type, bool> predicate = t => t.IsGenericType && (t.GetGenericTypeDefinition() == interfaceType); 71: if (!predicate(queryType)) 72: { 73: return queryType.GetInterfaces().FirstOrDefault<Type>(predicate); 74: } 75: return queryType; 76: } 77: }
如上面的代码片断所示,在BindModel方法中我们通过调用ExtractGenericInterface判断目标类型是否实现了IEnumerable<T>接口,如果实现了该接口则提取泛型元素类型。针对集合的Model绑定实现在方法BindCollectionModel中,我们按照数组绑定的方式得的针对目标集合对象的所有元素对象,并将其添加到一个List<object>对象中,然后调用ReplaceHelper 的静态方法ReplaceCollection将该列表中的元素拷贝到预先创建的Model对象中。定义在ReplaceHelper的静态方法ReplaceCollection定义如下:
1: internal static class ReplaceHelper 2: { 3: private static MethodInfo replaceCollectionMethod = typeof(ReplaceHelper).GetMethod("ReplaceCollectionImpl", BindingFlags.Static |BindingFlags.NonPublic); 4: 5: public static void ReplaceCollection(Type collectionType, object collection, object newContents) 6: { 7: replaceCollectionMethod.MakeGenericMethod(new Type[] { collectionType }).Invoke(null, new object[] { collection, newContents }); 8: } 9: private static void ReplaceCollectionImpl<T>(ICollection<T> collection, IEnumerable newContents) 10: { 11: collection.Clear(); 12: if (newContents != null) 13: { 14: foreach (object obj2 in newContents) 15: { 16: T item = (obj2 is T) ? ((T)obj2) : default(T); 17: collection.Add(item); 18: } 19: } 20: } 21: }
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索数组
, object
, return
, 字典数组
, 类型
, ienumerable
, 模拟类机制
asp数据字典
,以便于您获取更多的相关知识。