以DefaultModelBinder为例

为简单模型绑定(BindSimpleModel)和复杂模型绑定(BindComplexModel)

 public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException("bindingContext");
}
bool flag = false;
if (!string.IsNullOrEmpty(bindingContext.ModelName) && !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))
{
.......
}
if (!flag)
{
.......
if (valueProviderResult != null)
{
return this.BindSimpleModel(controllerContext, bindingContext, valueProviderResult);
}
}
if (!bindingContext.ModelMetadata.IsComplexType)
{
return null;
}
return this.BindComplexModel(controllerContext, bindingContext);
}
 

简单类型

简单类型就是直接通过ValueProviderResult valueProviderResult = bindingContext.UnvalidatedValueProvider.GetValue(bindingContext.ModelName, skipValidation);获取Result,直接通过BindSimpleModel返回RawValue值。

 internal object BindSimpleModel(ControllerContext controllerContext, ModelBindingContext bindingContext, ValueProviderResult valueProviderResult)
{
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
if (bindingContext.ModelType.IsInstanceOfType(valueProviderResult.RawValue))
{
return valueProviderResult.RawValue;
}
if (bindingContext.ModelType != typeof(string))
{
if (bindingContext.ModelType.IsArray)
{
return ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType);
}
Type type = TypeHelpers.ExtractGenericInterface(bindingContext.ModelType, typeof(IEnumerable<>));
if (type != null)
{
object o = this.CreateModel(controllerContext, bindingContext, bindingContext.ModelType);
Type collectionType = type.GetGenericArguments()[];
Type destinationType = collectionType.MakeArrayType();
object newContents = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, destinationType);
if (typeof(ICollection<>).MakeGenericType(new Type[] { collectionType }).IsInstanceOfType(o))
{
CollectionHelpers.ReplaceCollection(collectionType, o, newContents);
}
return o;
}
}
return ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType);
}
 

复杂类型

绑定类型为复杂类型是绑定属性

 internal object BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
...
this.BindComplexElementalModel(controllerContext, bindingContext, model);
...
} 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);
}
}

遍历属性描述进行绑定

 private void BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
foreach (PropertyDescriptor descriptor in this.GetFilteredModelProperties(controllerContext, bindingContext))
{
this.BindProperty(controllerContext, bindingContext, descriptor);
}
}

这时又会把bindingContext.ModelName和propertyDescriptor.Name进行组合成为新的前缀进行值得获取,并且获取新的ModelBindingContext进行绑定

 protected virtual void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
{
string prefix = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
if (bindingContext.ValueProvider.ContainsPrefix(prefix))
{
IModelBinder propertyBinder = this.Binders.GetBinder(propertyDescriptor.PropertyType);
object obj2 = propertyDescriptor.GetValue(bindingContext.Model);
ModelMetadata metadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
metadata.Model = obj2;
ModelBindingContext context = new ModelBindingContext {
ModelMetadata = metadata,
ModelName = prefix,
ModelState = bindingContext.ModelState,
ValueProvider = bindingContext.ValueProvider
};
object obj3 = this.GetPropertyValue(controllerContext, context, propertyDescriptor, propertyBinder);
......
}
 

集合类型、数组类型

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()判断是否为数组和集合

 internal object BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
......
Type type7 = TypeHelpers.ExtractGenericInterface(modelType, typeof(IEnumerable<>));
if (type7 != null)
{
Type type8 = type7.GetGenericArguments()[];
if (typeof(ICollection<>).MakeGenericType(new Type[] { type8 }).IsInstanceOfType(model))
{
ModelBindingContext context6 = new ModelBindingContext();
if (func2 == null)
{
func2 = () => model;
}
context6.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(func2, modelType);
context6.ModelName = bindingContext.ModelName;
context6.ModelState = bindingContext.ModelState;
context6.PropertyFilter = bindingContext.PropertyFilter;
context6.ValueProvider = bindingContext.ValueProvider;
ModelBindingContext context5 = context6;
return this.UpdateCollection(controllerContext, context5, type8);
}
}
......
}

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 == )
{
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的隐藏框代表索引项,下面再是具体的这个索引下面的数据项

 <input name="index" type="hidden" value="first" />
<input name="index" type="hidden" value="second" /> <input name="[first].UserName" type="text" value="" />
<input name="[first].Password" type="password" value="" />
<input name="[second].UserName" type="text" value="" />
<input name="[second].Password" type="password" value="" />

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

 [HttpPost]
public ActionResult LogOn(List<LogOnModel> UserName)
{
return View();
}

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

NameValueCollectionValueProvider类获取key值

 

字典类型

 internal object BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
......
Type type4 = TypeHelpers.ExtractGenericInterface(modelType, typeof(IDictionary<,>));
if (type4 != null)
{
Type[] genericArguments = type4.GetGenericArguments();
Type keyType = genericArguments[];
Type valueType = genericArguments[];
ModelBindingContext context4 = new ModelBindingContext();
if (modelAccessor == null)
{
modelAccessor = () => model;
}
context4.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(modelAccessor, modelType);
context4.ModelName = bindingContext.ModelName;
context4.ModelState = bindingContext.ModelState;
context4.PropertyFilter = bindingContext.PropertyFilter;
context4.ValueProvider = bindingContext.ValueProvider;
ModelBindingContext context3 = context4;
return this.UpdateDictionary(controllerContext, context3, keyType, valueType);
}
......
}

字典绑定同样是按照整数和字符串索引来来进行分组的,举例整数索引[0],字典的键的前缀为[0].key,值得前缀为[0].value,判断是否在数据源中有这些匹配项,如果有再进行后续的绑定操作,看到源码我们知道分别获取了键的ModelBindingContext和值得ModelBindingContext分别进行键和值的参数获取,binder.BindModel(controllerContext, context);为MVC中所有数据类型的获取参数的具体执行者,通过传入一个ModelBindingContext来执行的

 internal object UpdateDictionary(ControllerContext controllerContext, ModelBindingContext bindingContext, Type keyType, Type valueType)
{
bool flag;
IEnumerable<string> enumerable;
GetIndexes(bindingContext, out flag, out enumerable);
IModelBinder binder = this.Binders.GetBinder(keyType);
IModelBinder binder2 = this.Binders.GetBinder(valueType);
List<KeyValuePair<object, object>> newContents = new List<KeyValuePair<object, object>>();
foreach (string str in enumerable)
{
string prefix = CreateSubIndexName(bindingContext.ModelName, str);
string str3 = CreateSubPropertyName(prefix, "key");
string str4 = CreateSubPropertyName(prefix, "value");
if (!bindingContext.ValueProvider.ContainsPrefix(str3) || !bindingContext.ValueProvider.ContainsPrefix(str4))
{
if (!flag)
{
continue;
}
break;
}
ModelBindingContext context = new ModelBindingContext {
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, keyType),
ModelName = str3,
ModelState = bindingContext.ModelState,
ValueProvider = bindingContext.ValueProvider
};
object obj2 = binder.BindModel(controllerContext, context);
AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, str3, keyType, obj2);
if (keyType.IsInstanceOfType(obj2))
{
ModelBindingContext context2 = new ModelBindingContext {
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, valueType),
ModelName = str4,
ModelState = bindingContext.ModelState,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
object obj3 = binder2.BindModel(controllerContext, context2);
AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, str4, valueType, obj3);
KeyValuePair<object, object> item = new KeyValuePair<object, object>(obj2, obj3);
newContents.Add(item);
}
}
if (newContents.Count == )
{
return null;
}
object model = bindingContext.Model;
CollectionHelpers.ReplaceDictionary(keyType, valueType, model, newContents);
return model;
}

字典类型View视图代码

 <input name="[0].key" type="text" value="" />
<input name="[0].value.UserName" type="text" value="" />
<input name="[0].value.Password" type="password" />
<input name="[0].value.RememberMe" type="checkbox" value="true" /> <input name="[1].key" type="text" value="" />
<input name="[1].value.UserName" type="text" value="" />
<input name="[1].value.Password" type="password" />
<input name="[1].value.RememberMe" type="checkbox" value="true" />

Controller

 [HttpPost]
public ActionResult LogOn(Dictionary<string, LogOnModel> model)
{
return View();
}

asp.net MVC 4.0 Controller回顾——ModelBinding实现过程的更多相关文章

  1. ASP.NET MVC 3.0 Controller基础

    ASP.NET MVC 3.0 Controller基础   1.Controller类与方法 Controller(控制器)是ASP.NET MVC的核心,负责处理浏览器请求,并作出响应.Cotro ...

  2. asp.net MVC 4.0 View回顾——布局页与分部页

    asp.net MVC 4.0中总结 视图里加载部分视图几种方法 @RenderPage() 但它不能使用 原来视图的 Model 和 ViewData ,只能通过参数来传递. @RenderPage ...

  3. 返璞归真 asp.net mvc (7) - asp.net mvc 3.0 新特性之 Controller

    原文:返璞归真 asp.net mvc (7) - asp.net mvc 3.0 新特性之 Controller [索引页][源码下载] 返璞归真 asp.net mvc (7) - asp.net ...

  4. (转)ASP.NET Mvc 2.0 - 1. Areas的创建与执行

    转自:http://www.cnblogs.com/terrysun/archive/2010/04/13/1711218.html ASP.NET Mvc 2.0 - 1. Areas的创建与执行 ...

  5. 返璞归真 asp.net mvc (13) - asp.net mvc 5.0 新特性

    [索引页][源码下载] 返璞归真 asp.net mvc (13) - asp.net mvc 5.0 新特性 作者:webabcd 介绍asp.net mvc 之 asp.net mvc 5.0 新 ...

  6. 从零开始学习ASP.NET MVC 1.0

    转自:http://www.cnblogs.com/zhangziqiu/archive/2009/02/27/ASPNET-MVC-1.html <从零开始学习ASP.NET MVC 1.0& ...

  7. ASP.NET MVC 4.0的Action Filter

    有时候你想在调用action方法之前或者action方法之后处理一些逻辑,为了支持这个,ASP.NET MVC允许你自定义创建action过滤器.Action过滤器是自定义的Attributes,用来 ...

  8. 返璞归真 asp.net mvc (8) - asp.net mvc 3.0 新特性之 Model

    原文:返璞归真 asp.net mvc (8) - asp.net mvc 3.0 新特性之 Model [索引页][源码下载] 返璞归真 asp.net mvc (8) - asp.net mvc ...

  9. 返璞归真 asp.net mvc (12) - asp.net mvc 4.0 新特性之移动特性

    原文:返璞归真 asp.net mvc (12) - asp.net mvc 4.0 新特性之移动特性 [索引页][源码下载] 返璞归真 asp.net mvc (12) - asp.net mvc ...

随机推荐

  1. 设置win7资源管理器启动时的默认位置-windows-操作系统-网页教学网

    设置win7资源管理器启动时的默认位置-windows-操作系统-网页教学网 如何设置win7资源管理器启动时的默认位置?我不太习惯 Win 7 的资源管理器默认总是打开库,我还是喜欢资源管理器打开树 ...

  2. 安装python Matplotlib 库

    转:使用 python Matplotlib 库 绘图 及 相关问题  使用 python Matplotlib 库绘图      转:http://blog.csdn.net/daniel_ustc ...

  3. DropDownList绑定数据库

    this.DropDownList_设备列表.DataSource = dt_eq;//设置数据源 this.DropDownList_设备列表.DataTextField = "equip ...

  4. 新安装的 ubuntu 下 make menuconfig 报错

    环境:Ubtuntu 12.04 LTS 新安装的ubuntu 出现不能使用make menuconfig. 1.sudo apt-get update 更新软件 2.安装下面的软件 sudo apt ...

  5. 前端编码规范 -- css篇

    合理的避免使用ID 一般情况下ID不应该被应用于样式. ID的样式不能被复用并且每个页面中你只能使用一次ID. 使用ID唯一有效的是确定网页或整个站点中的位置. 尽管如此,你应该始终考虑使用class ...

  6. servlet之doPost()、doGet()

    1.doGet和doPost方法的具体应用?即在什么时候程序调用doGet方法,什么时候程序执行doPost方法? HttpServlet是从GenericServlet继承而来,因此HttpServ ...

  7. 小R的棋子

    小R的棋子(dp) 数轴上有 n 个位置可以摆放棋子,标号为1,2,3...n.小 R 现在要在一些位置摆放棋子,每个位置最多摆放一个棋子,摆放棋子的总数没有限制.小 R 不希望他摆放的棋子过于拥挤, ...

  8. 2017-10-1 清北刷题冲刺班a.m

    位运算1 (bit) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK拥有一个十进制的数N.它赋予了N一个新的意义:将N每一位都拆开来后再加起来就是N所拥 ...

  9. 树形DP 洛谷P2014 选课

    洛谷P2014 选课 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门 ...

  10. java中的String,StringBuffrer,Stringbuilder的区别

    简单描述下 效率:StringBuilder>StringBuffer>String 使用场景: 如果要操作少量的数据用 = String 单线程操作字符串缓冲区 下操作大量数据 = St ...