zoukankan      html  css  js  c++  java
  • 【ASP.NET MVC 学习笔记】- 16 Model Binding(模型绑定)

    本文参考:http://www.cnblogs.com/willick/p/3424188.html

    1、Model Binding是Http请求和Action方法之间的桥梁,是MVC框架根据Http请求创建.NET对象的过程。它根据Action方法中的Model对象的类型创建,NET对象,并将Http请求数据经过转换赋给该对象。

    2、Model Binding是从路由引擎接收和处理Http请求后开始的。例如:

    public static void RegisterRoutes(RouteCollection routes) 
    { 
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 
        routes.MapRoute( 
            name: "Default", 
            url: "{controller}/{action}/{id}", 
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
        );
    }
    
    //1、当请求/Home/Index/1时,路由系统把最后一个片段1赋值给id变量。
    //2、Action Invoker通过路由信息得知当前请求需要Index这个Action来处理;
    //3、Action Invoker通过Model Binder(模型绑定器)来创建调用Action方法需要的数据对象;
    //4、最后Action Invoker将数据对象赋值给Index这个Action的参数,再调用这个Action完成Http请求。

    3、Model Binder实现了下列接口:

    namespace System.Web.Mvc 
    { 
        public interface IModelBinder 
        { 
            object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext); 
        } 
    }

       在MVC中可以有多个Model Binder,每个Binder都负责一种或多种类型的Model。当Action Invoker调用Action方法时,它先看这个Action方法需要的参数,然后为每个参数找到和参数类型对应的Model Binder,然后调用Model Binder的BindModel方法,BindModel方法再根据Action方法的参数名称从路由信息中获取参数值,最后把值提供给Action Invoker,由Action Invoker方法完成Action方法的调用。

    4、Model Binder可以形象的理解为把数据绑定到一个Model的工具。当Action Invoker没有找到自定义的Model Binder时,就使用系统默认的DefaultModelBinder,它通过以下顺序查找要绑定到Model的值,一旦找到就停止查找:

    • Request.Form,HTML form 元素提供的值。
    • RouteData.Values,通过应用程序路由提供的值。
    • Request.QueryString,所请求 URL 的 query string 值。
    • Request.Files,客户端上传的文件

        对于简单类型,DefaultModelBinder会通过System.ComponentModel命名空间下的TypeDescriptor类将其转换为和参数类型相同的类型。若转换失败则不会把值绑定到参数上。

        Tips:对于值类型,尽量使用可空类型或可选参数,以免报错。

    5、对于复合类型(指不能被TypeConverter转换的类型),DefaultModelBinder类通过反射获取该类型所有的公开属性,然后依次进行绑定。

    6、如果某个Action方法的参数的类型是某个对象的属性的类型,此时需要使用BindAttribute来告诉DefaultModelBinder只查找特定前缀的的名称:

    public ActionResult DisplayAddress([Bind(Prefix="HomeAddress")]Address address)
    {
        return View(address);
    }

        需要注意的是使用 Bind 特性指定了前缀后,需要提交的表单元素的 name 属性必须有该前缀才能被绑定。

        Bind 特性还有两个属性,Exclude 和 Include。它们可以指定在 Mdoel 的属性中,Binder 不查找或只查找某个属性,即在查找时要么只包含这个属性要么不包含这个属性。

    7、Model Binder 把请求提交的数据绑定到数组和集合模型上有非常好的支持。

    8、当 action 方法定义了参数时,Model Binding 的过程是自动的。我们也可以通过在Action方法中调用UpdateModel方法来对Model Binding的过程进行手动控制。示例:

    public ActionResult Address() 
    {
        IList<Address> addresses = new List<Address>();
        UpdateModel(addresses, new FormValueProvider(ControllerContext));
        return View(addresses);
    }

        示例指定只从Request.Form中查找数据。

    9、FormValueProvider实现了IValueProvier接口,是ValueProvider的一种。RouteData.Values、Request.QueryString 和 Request.Files 的 Value Provider 分别是 RouteDataValueProviderQueryStringValueProviderHttpFileCollectionValueProvider

    10、还有一种限制Model Binder数据来源的方法,示例:

    //用 Action 方法的某个集合类型的参数来指定并存储从某一个来源获取的数据,这个集合类型(示例的 FormCollection) 也是 IValueProvider 接口的一个实现。
    public
    ActionResult Address(FormCollection formData) { IList<Address> addresses = new List<Address>(); UpdateModel(addresses, formData); return View(addresses); }

    11、有时候用户会提交一些 和 model 对象的属性不匹配的数据,如不合法的日期格式或给数值类型提供文本值,这时候绑定会出现错误,Model Binder 会用 InvalidOperationException 来表示。可以通过 Controller.ModelState 属性找到具体的错误信息,然后反馈给用户:

    public ActionResult Address(FormCollection formData) 
    {
        IList<Address> addresses = new List<Address>();
        try 
        {
            UpdateModel(addresses, formData);
        }
        catch (InvalidOperationException ex) 
        {
            var allErrors = ModelState.Values.SelectMany(v => v.Errors);
            // do something with allErrors and provide feedback to user 
        }
        return View(addresses);
    }

    12、可以用 ModelState.IsValid 属性来检查提交的数据是否合法。

    13、自定义ValueProvider示例:

    //1、实现IValueProvier接口
    public class CountryValueProvider : IValueProvider 
    {
        public bool ContainsPrefix(string prefix)
        {
            return prefix.ToLower().IndexOf("country") > -1;
        }
    
        public ValueProviderResult GetValue(string key) 
        {
            if (ContainsPrefix(key))
                return new ValueProviderResult("China", "China", CultureInfo.InvariantCulture);
            else
                return null;
        }
    }
    
    //2、自定义ValueProviderFactory来实例化自定义的ValueProvider
    public class CustomValueProviderFactory : ValueProviderFactory 
    {
        public override IValueProvider GetValueProvider(ControllerContext controllerContext) 
        {
            return new CountryValueProvider();
        }
    }
    
    //3、在Global类的Applcation_Start方法中进行注册
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        //通过 ValueProviderFactories.Factories 静态集合的 Insert 方法注册了我们的 CustomValueProviderFactory 类。
    //Insert 方法中的 0 参数保证 Binder 将首先使用自定义的类来提供值。
    //如果我们想在其他 value provider 不能提供值的时候使用,那么我们可以使用 Add 方法:ValueProviderFactories.Factories.Add(new CustomValueProviderFactory());
    ValueProviderFactories.Factories.Insert(0, new CustomValueProviderFactory()); }

    14、自定义Model Binder示例:

    //1、实现IModelBinder接口
    public class AddressBinder : IModelBinder 
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
        {
            Address model = (Address)bindingContext.Model ?? new Address();
            model.City = GetValue(bindingContext, "City");
            model.Country = GetValue(bindingContext, "Country");
            return model;
        }
    
        private string GetValue(ModelBindingContext context, string name) 
        {
            name = (context.ModelName == "" ? "" : context.ModelName + ".") + name;
            ValueProviderResult result = context.ValueProvider.GetValue(name);
            if (result == null || result.AttemptedValue == "") 
                return "<Not Specified>";
            else 
                return (string)result.AttemptedValue;
        }
    }
    
    //2、注册
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        ModelBinders.Binders.Add(typeof(Address), new AddressBinder());
    }
  • 相关阅读:
    降龙十八掌之三:(见龙在田)优化查询性能
    完整的项目工程目录结构
    降龙十八掌之一:(亢龙有悔)SQL Server Profiler和数据库引擎优化顾问
    ASP.NET状态管理的总结
    LINQ 图解
    获取IP城市
    Eclipse 编译StanfordNLP
    Centos JAVA Eclipse
    关掉PUTTY后,进程仍可以运行。
    centos lnmp 安装笔记
  • 原文地址:https://www.cnblogs.com/wangwust/p/6392587.html
Copyright © 2011-2022 走看看