zoukankan      html  css  js  c++  java
  • mvc源码解读(18)数据绑定组件ModelBinder之DefaultModelBinder

         前面的文章中我们已经讲了三种的ModelBinder组件,在讲第四种ModelBinder组件之前,我想解释一下什么是ModelBinder,顾名思义就是模型绑定,就是浏览器发送Http请求时根据相关的数据创建对象的过程,比如我们每一次请求一个action的时候,创建这个action参数对象的过程就可以叫做模型绑定,因此我们可以认为ModelBinder就是生成匹配的action参数的值。

         通过前面的文章介绍我们可以发现,mvc里面是有很多个ModelBinder,每一个ModelBinder可以绑定一个或是多个Model。当我们请求一个action的时候,会首先查找定义在action里面的参数并同时找到对应的负责每一个参数类型的ModelBinder,如果找不到的话,就会调用系统默认的ModelBinder。

         DefaultModelBinder类里面有很多个方法,我们如何来跟踪里面的逻辑判断呢,这里有一个技巧,我们可以自己定义一个ModelBinder,让这个自定义的ModelBinder继承DefaultModelBinder,并重写里面的方法即可。我们来一一分析:

        (1)绑定简单的数据类型:一般包括int,string.......这些简单的类型,如我们请求的Action为MyInt(int age),先来看看我们的view:

    相对应的Action如下:

    点击提交按钮之后,执行的是BindSimpleModel方法,并通过ConvertProviderResult方法直接将value赋值给了age参数:

     internal object BindSimpleModel(ControllerContext controllerContext, ModelBindingContext bindingContext, ValueProviderResult valueProviderResult)

    {

          .............................................................
         object model = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType);

         return model;

    }

    假如我们在文本框中输入12,按提交的时候,ConvertProviderResult方法里面执行结果如下:

          当然如果是数组的话,int[]和string[]类型的绑定情况有些不一样,int[]类型的数组和int,string。。。。。一样都是通过ConvertProviderResult方法获取到参数的值,但是string[]参数里面的值是通过ValueProviderResult里面的RawValue获取到的,相关的demo如下:

    MyString()定义如下:

    在三个文本框中输入:老A,老B,老C,运行截图如下:

      如果Action的参数是一下类型如:List<T>(其中T不是Model,而是简单的数据类型)等集合或是array数组的话,执行的都是BindSimpleModel方法,调用的对应的获取值得方法,细节不阐述。

    (2)复杂类型:mvc框架对于复杂类型的数据绑定采用的是递归的方法。复杂类型的情况有很多种,我们一一来看:

                 (2.1):如果Action的参数形式如下:

    就是直接将Model作为参数的话,将执行BindComplexModel绑定复杂模型方法,当然开始的时候如果模型元数据里面的ModelMetadata.Model为空的话,则首先要创建一个Model,随着执行BindComplexElementalModel方法:

       BindComplexElementalModel(controllerContext, bindingContext, model);

    当然这里面会创建一个新的内部的绑定上下文对象,然后循环调用BindProperties绑定属性的方法:

     private void BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                IEnumerable<PropertyDescriptor> properties = GetFilteredModelProperties(controllerContext, bindingContext);
                foreach (PropertyDescriptor property in properties)
                {
                    BindProperty(controllerContext, bindingContext, property);
                }
            }

     这里面红色大代码实际上执行的又是BindSimpleModel方法,循环调用该方法,知道Model的属性被赋值为止。

         (2.2)如果Action的参数形式如下:

    紧接着创建一个强类型的Model的View,如下截图所示(只是其中的一小部分):

    运行之后会发现一个很坑爹的问题:注意看我们的运行时的断点情况:

    也就是没有获取到相应的数据,关键在于ContainsPrefix这个方法,该方法的定义如下:

      // 摘要:
            //     确定集合是否包含指定的前缀。
            //
            // 参数:
            //   prefix:
            //     要搜索的前缀。
            //
            // 返回结果:
            //     如果集合包含指定的前缀,则为 true;否则为 false。
            bool ContainsPrefix(string prefix);

    就是获取集合的指定的前缀,就比如说是复杂类型的参数,参数前面都是一般都是带有Model+属性的形式:Model.属性作为参数传递的,而我们view中并显示没有指定前缀,因此运行的最终的结果是:

    我们可以看到userInfo参数为null,并没有成功将客户端的值绑定到action的参数中(注意:客户端输入的数据的图没有贴出来)。

         既然如此我们改变一下策略,在view中我们定义如下:

    就是我们手动定义它的前缀,这里应该注意到:1:当action的参数如果是数组或者IEumerable<T>的,例如 Action(List<UserInfo> UserInfo),系统在内部内部创建一个List,进行数组绑定。数组绑定的时候,对数组中的key有几个约束条件:

         1.1:是以数字为index的,比如 [0].Name, [0].Age,  [0].Gender,[1].Name, [1].Age, [0].Gender。这里的数字必须是从0开始而且是连续的,当然这个条件一般比较难满足的,因为在实际编码中我们不可能自己洗去写这个索引,我们当然可以通过循环遍历的方法来确定key的值。

         我们再次运行一下程序,运行结果如下:

    我们可以看到已经成功将数据绑定到action参数中。这里面我们应该注意到,和action(Model model)形式的参数绑定差不多,action(List<Model> model)绑定时会首先创建一个List<>,再循环遍历Model的属性并赋值,再将model放入List集合中返回给客户端。其他的只要是IEumerable<T>,或者IDictionary<k,t>参数绑定原理都是一样的,这里不做参数。

        这里倒是有另外一个问题,如果一个Model中属性也是一个复杂类型的话,它的参数绑定又是如何的呢?

        我们重新修改一下我们的Model:

    Address也是一个实体,其中View如下:

          运行效果如下:

           遍历UserInfo所有属性的时候,如果有些属性本身也是复杂类型,则循环刚才所说的BindComplexModel方法,创建了Model执行后在执行BindSimpleModel,一步一步的赋值。

           绑定复杂对象的时候,需要能够区分这个key对应的是哪个对象上的属性值。比如上面的例子中,name="Address.city"就表明,这是UserInfo类型的Address属性的City属性和Province属性,默认的ModelBinder是通过点号和中括号来区分的,点号分隔开的一段段称为prefix即前缀,有关前缀的作用和意义暂且不述~~~~~~~~~~

  • 相关阅读:
    如何在Blog中使用feedburner管理RSS订阅
    网络基本功(八):细说TCP滑动窗口
    JAVA三大框架的各自作用
    NHibernate与EF(Entity Framework)的区别
    程序模拟浏览器请求及会话保持-python实现
    Netbeans7.4下搭建struts2.3.16
    解决Sqlite UTF-8中文数据格式在DOS窗口下中文乱码
    Android使用xml中定义的动画效果
    Android 之Activity切换动画效果
    设置ActioinBar 的背景色以及Title的字体颜色
  • 原文地址:https://www.cnblogs.com/ghhlyy/p/2996755.html
Copyright © 2011-2022 走看看