首页 > 代码库 > asp.net MVC 4.0 Controller回顾——ModelBinding过程

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 =http://www.mamicode.com/ bindingContext.PropertyMetadata[propertyDescriptor.Name]; 9                 metadata.Model = obj2;10                 ModelBindingContext context = new ModelBindingContext {11                     ModelMetadata =http://www.mamicode.com/ metadata,12                     ModelName = prefix,13                     ModelState = bindingContext.ModelState,14                     ValueProvider = bindingContext.ValueProvider15                 };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 =http://www.mamicode.com/ 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=http://www.mamicode.com/"" />2 <input name="[0].Password" type="password" />3 <input name="[0].RememberMe" type="checkbox" value=http://www.mamicode.com/"true" />4 5 <input name="[1].UserName" type="text" value=http://www.mamicode.com/"" />6 <input name="[1].Password" type="password" />7 <input name="[1].RememberMe" type="checkbox" value=http://www.mamicode.com/"true" />

Controller调用方法参数为List<T>

1 [HttpPost]2 public ActionResult LogOn(List<LogOnModel> UserName)3 {4     return View();5 }

上面分析UpdateCollection 时这时候模型绑定的前缀为 [0].UserName

NameValueCollectionValueProvider类获取key值

asp.net MVC 4.0 Controller回顾——ModelBinding过程