在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
以DefaultModelBinder为例 为简单模型绑定(BindSimpleModel)和复杂模型绑定(BindComplexModel) 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 }
简单类型就是直接通过ValueProviderResult valueProviderResult = bindingContext.UnvalidatedValueProvider.GetValue(bindingContext.ModelName, skipValidation);获取Result,直接通过BindSimpleModel返回RawValue值。 1 internal object BindSimpleModel(ControllerContext controllerContext, ModelBindingContext bindingContext, ValueProviderResult valueProviderResult) 2 { 3 bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); 4 if (bindingContext.ModelType.IsInstanceOfType(valueProviderResult.RawValue)) 5 { 6 return valueProviderResult.RawValue; 7 } 8 if (bindingContext.ModelType != typeof(string)) 9 { 10 if (bindingContext.ModelType.IsArray) 11 { 12 return ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType); 13 } 14 Type type = TypeHelpers.ExtractGenericInterface(bindingContext.ModelType, typeof(IEnumerable<>)); 15 if (type != null) 16 { 17 object o = this.CreateModel(controllerContext, bindingContext, bindingContext.ModelType); 18 Type collectionType = type.GetGenericArguments()[0]; 19 Type destinationType = collectionType.MakeArrayType(); 20 object newContents = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, destinationType); 21 if (typeof(ICollection<>).MakeGenericType(new Type[] { collectionType }).IsInstanceOfType(o)) 22 { 23 CollectionHelpers.ReplaceCollection(collectionType, o, newContents); 24 } 25 return o; 26 } 27 } 28 return ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType); 29 }
1 internal object BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 2 { 3 ... 4 this.BindComplexElementalModel(controllerContext, bindingContext, model); 5 ... 6 } 7 8 internal void BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, object model) 9 { 10 ModelBindingContext context = this.CreateComplexElementalModelBindingContext(controllerContext, bindingContext, model); 11 if (this.OnModelUpdating(controllerContext, context)) 12 { 13 this.BindProperties(controllerContext, context); 14 this.OnModelUpdated(controllerContext, context); 15 } 16 } 遍历属性描述进行绑定 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.相同数据项的数组绑定 作为数据源的NameValueCollection没有对key做唯一性约束,当参数类型为简单数据类型的数组或集合时,同一个key将对应多个值,这时key获取到的ValueProviderResult就要转换为数组或集合。 1 <input name="UserName" type="text" value="" />
2 <input name="UserName" type="text" value="" />
3 <input name="Password" type="password" />
4 <input name="Password" type="password" />
5 <input name="RememberMe" type="checkbox" value="true" />
6 <input name="RememberMe" type="checkbox" value="true" />
参数名称必须为name,因为 if (!string.IsNullOrEmpty(bindingContext.ModelName) && !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))通过,直接把参数名称作为key来获取值 注:一般来说参数类型为简单类型时参数名称必须也name相同,因为是直接用参数名称作为key去匹配取值的,其他数据类型的参数名称可以随便取(简单类型的数组或集合除外)
1 public ActionResult LogOn(List<int> UserName) 2 { 3 return View(); 4 } 在判断modelType是否和参数类型一致,如果一致直接返回,不一致转换为参数类型返回 2.整数和字符串索引的数组绑定 通过BindComplexModel方法TypeHelpers.ExtractGenericInterface()判断是否为数组和集合 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()首先判断是否是有[Index]为前缀的值,如果有获取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; } 注:查找的时候是按照 [索引],[索引].字段名进行检索的 可以看到匹配项依次是 "",[索引],[索引].字段名,因为查找的时候是按照这个顺序来进行的,如果有[0]就代表有索引为0的项的数据,就可以进行后续的当前项的模型绑定 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" />
字符串索引,设置Name=index的隐藏框代表索引项,下面再是具体的这个索引下面的数据项 1 <input name="index" type="hidden" value="first" /> 2 <input name="index" type="hidden" value="second" /> 3 4 <input name="[first].UserName" type="text" value="" /> 5 <input name="[first].Password" type="password" value="" /> 6 <input name="[second].UserName" type="text" value="" /> 7 <input name="[second].Password" type="password" value="" /> Controller调用方法参数为List<T> 1 [HttpPost] 2 public ActionResult LogOn(List<LogOnModel> UserName) 3 { 4 return View(); 5 } 上面分析UpdateCollection 时这时候模型绑定的前缀为 [0].UserName NameValueCollectionValueProvider类获取key值
1 internal object BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 2 { 3 ...... 4 Type type4 = TypeHelpers.ExtractGenericInterface(modelType, typeof(IDictionary<,>)); 5 if (type4 != null) 6 { 7 Type[] genericArguments = type4.GetGenericArguments(); 8 Type keyType = genericArguments[0]; 9 Type valueType = genericArguments[1]; 10 ModelBindingContext context4 = new ModelBindingContext(); 11 if (modelAccessor == null) 12 { 13 modelAccessor = () => model; 14 } 15 context4.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(modelAccessor, modelType); 16 context4.ModelName = bindingContext.ModelName; 17 context4.ModelState = bindingContext.ModelState; 18 context4.PropertyFilter = bindingContext.PropertyFilter; 19 context4.ValueProvider = bindingContext.ValueProvider; 20 ModelBindingContext context3 = context4; 21 return this.UpdateDictionary(controllerContext, context3, keyType, valueType); 22 } 23 ...... 24 } 字典绑定同样是按照整数和字符串索引来来进行分组的,举例整数索引[0],字典的键的前缀为[0].key,值得前缀为[0].value,判断是否在数据源中有这些匹配项,如果有再进行后续的绑定操作,看到源码我们知道分别获取了键的ModelBindingContext和值得ModelBindingContext分别进行键和值的参数获取,binder.BindModel(controllerContext, context);为MVC中所有数据类型的获取参数的具体执行者,通过传入一个ModelBindingContext来执行的 1 internal object UpdateDictionary(ControllerContext controllerContext, ModelBindingContext bindingContext, Type keyType, Type valueType) 2 { 3 bool flag; 4 IEnumerable<string> enumerable; 5 GetIndexes(bindingContext, out flag, out enumerable); 6 IModelBinder binder = this.Binders.GetBinder(keyType); 7 IModelBinder binder2 = this.Binders.GetBinder(valueType); 8 List<KeyValuePair<object, object>> newContents = new List<KeyValuePair<object, object>>(); 9 foreach (string str in enumerable) 10 { 11 string prefix = CreateSubIndexName(bindingContext.ModelName, str); 12 string str3 = CreateSubPropertyName(prefix, "key"); 13 string str4 = CreateSubPropertyName(prefix, "value"); 14 if (!bindingContext.ValueProvider.ContainsPrefix(str3) || !bindingContext.ValueProvider.ContainsPrefix(str4)) 15 { 16 if (!flag) 17 { 18 continue; 19 } 20 break; 21 } 22 ModelBindingContext context = new ModelBindingContext { 23 ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, keyType), 24 ModelName = str3, 25 ModelState = bindingContext.ModelState, 26 ValueProvider = bindingContext.ValueProvider 27 }; 28 object obj2 = binder.BindModel(controllerContext, context); 29 AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, str3, keyType, obj2); 30 if (keyType.IsInstanceOfType(obj2)) 31 { 32 ModelBindingContext context2 = new ModelBindingContext { 33 ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, valueType), 34 ModelName = str4, 35 ModelState = bindingContext.ModelState, 36 PropertyFilter = bindingContext.PropertyFilter, 37 ValueProvider = bindingContext.ValueProvider 38 }; 39 object obj3 = binder2.BindModel(controllerContext, context2); 40 AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, str4, valueType, obj3); 41 KeyValuePair<object, object> item = new KeyValuePair<object, object>(obj2, obj3); 42 newContents.Add(item); 43 } 44 } 45 if (newContents.Count == 0) 46 { 47 return null; 48 } 49 object model = bindingContext.Model; 50 CollectionHelpers.ReplaceDictionary(keyType, valueType, model, newContents); 51 return model; 52 } 字典类型View视图代码 1 <input name="[0].key" type="text" value="" /> 2 <input name="[0].value.UserName" type="text" value="" /> 3 <input name="[0].value.Password" type="password" /> 4 <input name="[0].value.RememberMe" type="checkbox" value="true" /> 5 6 <input name="[1].key" type="text" value="" /> 7 <input name="[1].value.UserName" type="text" value="" /> 8 <input name="[1].value.Password" type="password" /> 9 <input name="[1].value.RememberMe" type="checkbox" value="true" /> Controller 1 [HttpPost] 2 public ActionResult LogOn(Dictionary<string, LogOnModel> model) 3 { 4 return View(); 5 }
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论