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


    以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 }
  • 相关阅读:
    末学者笔记--KVM虚拟化存储管理(3)
    离线安装docker-ce
    OpenStack各组件的常用命令
    docker容器的基本命令
    nfs samba文件共享服务
    kvm虚拟机管理(创建、连接)
    虚拟化kvm的搭建
    python(pymysql操作数据库)
    三大特征 封装 继承 多态
    面向对象
  • 原文地址:https://www.cnblogs.com/raohuagang/p/3965556.html
Copyright © 2020-2023  润新知