asp.net MVC 4.0 Controller回顾——ModelBinding过程
以DefaultModelBinder为例
为简单模型绑定(BindSimpleModel)和复杂模型绑定(BindSimpleModel)
1 public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 2 { 3 if (bindingContext == null) 4 { 5 throw new ArgumentNullException("bindingContext"); 6 } 7 bool flag = false; 8 if (!string.IsNullOrEmpty(bindingContext.ModelName) && !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) 9 { 10 ....... 11 } 12 if (!flag) 13 { 14 ....... 15 if (valueProviderResult != null) 16 { 17 return this.BindSimpleModel(controllerContext, bindingContext, valueProviderResult); 18 } 19 } 20 if (!bindingContext.ModelMetadata.IsComplexType) 21 { 22 return null; 23 } 24 return this.BindComplexModel(controllerContext, bindingContext); 25 }
复杂类型 |
绑定类型为复杂类型是绑定属性
internal void BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, object model) { ModelBindingContext context = this.CreateComplexElementalModelBindingContext(controllerContext, bindingContext, model); if (this.OnModelUpdating(controllerContext, context)) { this.BindProperties(controllerContext, context); this.OnModelUpdated(controllerContext, context); } }
遍历属性描述进行绑定
1 private void BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) 2 { 3 foreach (PropertyDescriptor descriptor in this.GetFilteredModelProperties(controllerContext, bindingContext)) 4 { 5 this.BindProperty(controllerContext, bindingContext, descriptor); 6 } 7 }
这时又会把bindingContext.ModelName和propertyDescriptor.Name进行组合成为新的前缀进行值得获取,并且获取新的ModelBindingContext进行绑定
1 protected virtual void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) 2 { 3 string prefix = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name); 4 if (bindingContext.ValueProvider.ContainsPrefix(prefix)) 5 { 6 IModelBinder propertyBinder = this.Binders.GetBinder(propertyDescriptor.PropertyType); 7 object obj2 = propertyDescriptor.GetValue(bindingContext.Model); 8 ModelMetadata metadata = bindingContext.PropertyMetadata[propertyDescriptor.Name]; 9 metadata.Model = obj2; 10 ModelBindingContext context = new ModelBindingContext { 11 ModelMetadata = metadata, 12 ModelName = prefix, 13 ModelState = bindingContext.ModelState, 14 ValueProvider = bindingContext.ValueProvider 15 }; 16 object obj3 = this.GetPropertyValue(controllerContext, context, propertyDescriptor, propertyBinder); 17 ...... 18 }
集合类型、数组类型 |
1 internal object BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 2 { 3 ...... 4 Type type7 = TypeHelpers.ExtractGenericInterface(modelType, typeof(IEnumerable<>)); 5 if (type7 != null) 6 { 7 Type type8 = type7.GetGenericArguments()[0]; 8 if (typeof(ICollection<>).MakeGenericType(new Type[] { type8 }).IsInstanceOfType(model)) 9 { 10 ModelBindingContext context6 = new ModelBindingContext(); 11 if (func2 == null) 12 { 13 func2 = () => model; 14 } 15 context6.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(func2, modelType); 16 context6.ModelName = bindingContext.ModelName; 17 context6.ModelState = bindingContext.ModelState; 18 context6.PropertyFilter = bindingContext.PropertyFilter; 19 context6.ValueProvider = bindingContext.ValueProvider; 20 ModelBindingContext context5 = context6; 21 return this.UpdateCollection(controllerContext, context5, type8); 22 } 23 } 24 ...... 25 }
GetIndexes()获取Name=Index的IEnumerable<string>集合(字符串索引集合),再根据ModelName+[当前索引前缀],进行模型绑定,通过ValueProvider.ContainsPrefix(prefix)判断是否包含当前前缀的,重新获取elementType的ModelBindingContext进行模型绑定(elementType为集合类型,通过 type7.GetGenericArguments()[0]获取到)
internal object UpdateCollection(ControllerContext controllerContext, ModelBindingContext bindingContext, Type elementType) { bool flag; IEnumerable<string> enumerable; GetIndexes(bindingContext, out flag, out enumerable); IModelBinder binder = this.Binders.GetBinder(elementType); List<object> newContents = new List<object>(); foreach (string str in enumerable) { string prefix = CreateSubIndexName(bindingContext.ModelName, str); if (!bindingContext.ValueProvider.ContainsPrefix(prefix)) { if (!flag) { continue; } break; } ModelBindingContext context = new ModelBindingContext { ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, elementType), ModelName = prefix, ModelState = bindingContext.ModelState, PropertyFilter = bindingContext.PropertyFilter, ValueProvider = bindingContext.ValueProvider }; object obj2 = binder.BindModel(controllerContext, context); AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, prefix, elementType, obj2); newContents.Add(obj2); } if (newContents.Count == 0) { return null; } object model = bindingContext.Model; CollectionHelpers.ReplaceCollection(elementType, model, newContents); return model; }
View代码
1 @using System.Collections.ObjectModel
2 @model ObservableCollection<MvcSource.Models.LogOnModel>
3
4 @Html.LabelFor(m => m[0].UserName)
5 @Html.LabelFor(m => m[0].Password)
6 @Html.CheckBoxFor(m => m[0].RememberMe)
7
8 @Html.LabelFor(m => m[1].UserName)
9 @Html.LabelFor(m => m[1].Password)
10 @Html.CheckBoxFor(m => m[1].RememberMe)
生成出来的HTML源码
1 <input name="[0].UserName" type="text" value="" />
2 <input name="[0].Password" type="password" />
3 <input name="[0].RememberMe" type="checkbox" value="true" />
4
5 <input name="[1].UserName" type="text" value="" />
6 <input name="[1].Password" type="password" />
7 <input name="[1].RememberMe" type="checkbox" value="true" />
Controller调用方法参数为List<T>
1 [HttpPost] 2 public ActionResult LogOn(List<LogOnModel> UserName) 3 { 4 return View(); 5 }
上面分析UpdateCollection 时这时候模型绑定的前缀为 [0].UserName
NameValueCollectionValueProvider类获取key值
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。