asp.net mvc源码分析-DefaultModelBinder 集合绑定
接着上篇关于数据绑定的asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证里面只讲了复杂数据类型的绑定,还有上面集合、字典等这些数据这么绑定的了。说到集合绑定其实网上已经有很多关于它的介绍通过实例模拟ASP.NET MVC的Model绑定机制:数组。这个我先举一个使用例子吧:
后端代码:

前端代码:

运行结果:

好,现在让我们来看看集合的数据究竟是怎么绑定的吧:
在BindComplexModel方法中有这么一段:
Type enumerableType = TypeHelpers.ExtractGenericInterface(modelType, typeof(IEnumerable<>));
if (enumerableType != null) {
Type elementType = enumerableType.GetGenericArguments()[0];
Type collectionType = typeof(ICollection<>).MakeGenericType(elementType);
if (collectionType.IsInstanceOfType(model)) {
ModelBindingContext collectionBindingContext = new ModelBindingContext() {
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, modelType),
ModelName = bindingContext.ModelName,
ModelState = bindingContext.ModelState,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
object collection = UpdateCollection(controllerContext, collectionBindingContext, elementType);
return collection;
}
}
在这里TypeHelpers.ExtractGenericInterface方法主要是用来检查modelType是否是一个集合类型,Type elementType = enumerableType.GetGenericArguments()[0];这句就是获取集合元素类型,在这个例子中它是UserInfo的类型。 Type collectionType = typeof(ICollection<>).MakeGenericType(elementType);这句也很好理解,就是创建一个elementType的集合类型,后面再看看当前的model是否是这个集合类型的实例,要注意一下默认这里的model是没有什么实际内容的,但是它也不为null,后面紧接着创建新的ModelBindingContext,最后调用UpdateCollection方法,看来绑定的关键还是在UpdateCollection方法里面:
public class CollectionModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { Type type = bindingContext.ModelType; object model = Activator.CreateInstance(bindingContext.ModelType); List<object> modelList = new List<object>(); if (type.IsGenericType) { Type elementType = type.GetGenericArguments()[0]; Type collectionType = typeof(ICollection<>).MakeGenericType(elementType); if (collectionType.IsInstanceOfType(model)) { NameValueCollection formData = HttpContext.Current.Request.Form; List<string> keys = new List<string>(); foreach (var item in formData.Keys) { keys.Add(item.ToString()); } foreach (PropertyInfo p in elementType.GetProperties()) { List<string> usekeys = keys.Where(x => x.Contains(p.Name)).ToList(); while (modelList.Count < usekeys.Count) { object obj = Activator.CreateInstance(elementType); modelList.Add(obj); } for (int i = 0; i < usekeys.Count; i++) { p.SetValue(modelList[i], formData[usekeys[i]], null); } } } ReplaceCollection(elementType, model, modelList); return model; } return null; } private static readonly MethodInfo _replaceCollectionMethod = typeof(CollectionModelBinder).GetMethod("ReplaceCollectionImpl", BindingFlags.Static | BindingFlags.NonPublic); [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static void ReplaceCollection(Type collectionType, object collection, object newContents) { MethodInfo targetMethod = _replaceCollectionMethod.MakeGenericMethod(collectionType); targetMethod.Invoke(null, new object[] { collection, newContents }); } private static void ReplaceCollectionImpl<T>(ICollection<T> collection, IEnumerable newContents) { collection.Clear(); if (newContents != null) { foreach (object item in newContents) { // if the item was not a T, some conversion failed. the error message will be propagated, // but in the meanwhile we need to make a placeholder element in the array. T castItem = (item is T) ? (T)item : default(T); collection.Add(castItem); } } } }view调用不变,在Action调用的时候增加一个ModelBinder特性。 public ActionResult Index([ModelBinder(typeof(CollectionModelBinder))]List<UserInfo> users)