在ASP.NET MVC中,每个请求都被映射到一个Action方法,我们可以在action的方法中定义相应类型的参数,View中通过post、get方式提交的request参数,只要名称一致就会对应到相应的action参数,一切似乎理所当然,但是请注意我们的http是基于文本协议的,提交上去的参数应该是被认为是字符串形式,但是我们可以在action中定义string类型之外的其他参数,如int,datetime。在提交到action进行请求的过程里肯定有一个转换。

MVC框架里实现这个转换的就是DefaultModelBinder,DefaultModelBinder实现了IModelBinder接口,该接口的代码如下:

public interface IModelBinder {
       object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext);
   }

DefaultModelBinder能实现如下类型的转换:

如果研究MVC的源码我们可以发现, 调用action最终是由ControllerActionInvoker类的InvokeAction方法实现,这个方法里首先会获取调用action所需要的参数,即GetParametersValues方法。这个方法会有一个IModelBinder的选择过程和原则:

  • 尝试从附加在参数的CustomModelBinderAttribute特性获取
  • 尝试从ModelBinders.Binders集合中按参数类型检索
  • 尝试从附加在参数的类型的CustomModelBinderAttribute特性获取
  • 使用ModelBinders.Binders.DefaultBinder

如果DefaultModelBinder不能实现我们所需要的转换功能,则我们可以自己定义实现IModelBinder接口的ModelBinder。这种情况现在暂时还没碰到过,通过老赵的一片博客的例子来联系,例如当我们从客户端接口一个可以转换成datetime的类型时,我们希望可以自动转换成某种形式的DateTime。

public class DateTimeModelBinder : IModelBinder
{
public string Format { get; private set; } public DateTimeModelBinder(string format)
{
this.Format = format;
} public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object value=bindingContext.ValueProvider.GetValue(bindingContext.ModelName).RawValue;
if(value is string)
{
return DateTime.ParseExact((string)value, this.Format, null);
}
else
{
return value;
}
}
}

定义好了ModelBinder,怎么让框架能够使用它,上面讲到了框架中IModelBinder的选择过程和原则。因此我们可以有这么几种实现方法:

1.利用CustomerModelBinderAttribute,写一个类来实现它的GetBinder方法。

public class DateTimeAttribute : CustomModelBinderAttribute
{
public string Format { get; private set; } public DateTimeAttribute(string format)
{
this.Format = format;
} public override IModelBinder GetBinder()
{
return new DateTimeModelBinder(this.Format);
} }
public ActionResult Test([DateTime("yyyy-MM-dd")]DateTime date)

2.通过在全局文件中想ModelBinders集合添加我们自定义的ModelBinder

MvcAppDemo.Models.DateTimeModelBinder dateTimeBinder = new Models.DateTimeModelBinder("yyyy-MM-dd");
            ModelBinders.Binders.Add(typeof(DateTime), dateTimeBinder);
public ActionResult Test(DateTime date)

3.通过向我们参数的类型添加CustomerModelBinderAttribute来实现,但是这里DateTime是基本类型,这里通过另外一个例子来实现。蛋疼下,自己定义一个实体,然后实现这个实体的转换。

Blog实体:

[BlogBinder]
public class Blog
{
public string Title { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public DateTime PostDate { get; set; }
}

自定义的ModelBinder:

public class BlogBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object model = Activator.CreateInstance(bindingContext.ModelType);
PropertyDescriptorCollection col = TypeDescriptor.GetProperties(model);
foreach (PropertyDescriptor item in col)
{
string value=bindingContext.ValueProvider.GetValue(item.Name).AttemptedValue;
item.SetValue(model, Convert.ChangeType(value,item.PropertyType));
}
return model;
}
}

实现CustomerModelAttribute:

public class BlogBinderAttribute : CustomModelBinderAttribute
{
public override IModelBinder GetBinder()
{
return new BlogBinder();
}
}

其实实现自定义ModelBinder的思路很简单,就是通过反射获取我们的绑定类型的各个成员,接下来以各个成员的名称为键到bindingContext中去找值,接下来进行类型转换,然后再赋值给我们的模型。这个实现的过程中有碰到两个问题

1.类型转换,如果我不适用Convert.ChangeType(object value,Type convertType),而是使用TypeConvert.ConvertTo(object value,destinationType),在转换DateTime类型时会出错?这时为什么?

2.在使用反射进行转换的时候可以利用TypeDescriptor.GetProperties(model)和PropertyDescriptor,自己使用Type和PropertyInfo也同样可以使用,在MVC里面很多地方使用了TypeDescriptor,或者可以见到以Descriptor为后缀的类。TypeDescriptor和Type有什么区别?

MVC系统学习3—ModelBinder的更多相关文章

  1. MVC系统学习1—MVC执行流程

    用MVC来做开发也有一段时间了,但是感觉一直没入门,就徘徊在似懂非懂的层次,和去年刚毕业学习WebForm时一样,当时通过张子阳老兄的几篇文章,明白了请求处理流程,页面生命周期才真正明白了WebFor ...

  2. MVC系统学习5——验证

    其实关于Mvc的验证在上一篇已经有讲过一些了,可以通过在我们定义的Model上面添加相应的System.ComponentModel.DataAnnotations空间下的验证属性.在服务器端通过Mo ...

  3. MVC系统学习6—Filter

    Mvc的过滤器是特性类,可以使我们在执行Action之前,执行Action之后,执行Action发生异常时,编写相关的处理代码实现某些逻辑.下面是四个基本的Filter接口. 上面这四个基本的Filt ...

  4. MVC系统学习4—ModelMetaData

    在Mvc R2中,新引入了一些扩展方法,如后面带一个for的方法,这些扩展方法会根据Model的属性自定生成相应的Html元素,如Html.EditFor(Model=>Model.IsAppr ...

  5. MVC系统学习2—MVC路由

    在MVC下不是通过对物理文件的映射来实行访问的,而是通过定义后的路由Url来实现访问的.在前一篇讲到我们是在全局文件下进行路由配置. routes.MapRoute(                & ...

  6. Mvc系统学习9——Areas学习

    在Mvc2.0中,新增加了一个特性就是Areas.在没有有使用Areas的情况下,我们的Mvc项目组织是下面这样的.当项目庞大的时候,Controllers,Model,View文件下下面势必会有很多 ...

  7. MVC系统学习7—Action的选择过程

    在Mvc源码的ControllerActionInvoker的InvokeAction方法里面有一个FindAction方法,FindAction方法在ControllerDescriptor里面定义 ...

  8. MVC系统学习8——AsyncController

    关于为什么使用异步Controller,这里不做备忘,三岁小孩都懂.主要的备忘是如何使用AsyncController. //这个action以Async结尾,并且返回值是void public vo ...

  9. MVC学习系列——ModelBinder扩展

    在MVC系统中,我们接受数据,运用的是ModelBinder 的技术. MVC学习系列——ActionResult扩展在这个系列中,我们自定义了XmlResult的返回结果. 那么是不是意味着能POS ...

随机推荐

  1. IOS基础之 (七) 分类Category

    一 Category 分类:Category(类目,类别) (OC有) 命名:原来的类+类别名(原来的类名自动生成,只要写后面的类别名,一般以模块名为名.比如原来类 Person,新建分类 Ct,新建 ...

  2. POJ1753Flip Game(DFS + 枚举)

    Flip Game Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 37050   Accepted: 16122 Descr ...

  3. 蓝桥T291(BFS + 输出路径)

    http://lx.lanqiao.org/problem.page?gpid=T291 学霸的迷宫   时间限制:1.0s   内存限制:256.0MB      问题描述 学霸抢走了大家的作业,班 ...

  4. HD1205吃糖果(鸽巢、抽屉原理)

    吃糖果 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submiss ...

  5. MVC执行顺序

    MVC在底层和传统的asp.net是一致的,在底层之上,相关流程如下: 1)Global.asax里,MvcApplication对象的Application_Start()事件中,调用 RouteC ...

  6. json 数据交换格式与java

    http://wiki.mbalib.com/wiki/数据交换 数据交换是指为了满足不同信息系统之间数据资源的共享需要,依据一定的原则,采取相应的技术,实现不同信息系统之间数据资源共享的过程. 数据 ...

  7. linux学习之用户管理

    用户管理是在root用户下进行相关操作的 1.配置文件路径:         保存用户信息的文件:/etc/passwd         保存密码的文件:/etc/shadow         保存用 ...

  8. 将对象转为数组方法:延伸array_map函数在PHP类中调用内部方法

    public static function objectToArray($d) { if (is_object($d)) { $d = get_object_vars($d); } if (is_a ...

  9. asp.net修改web.config文件

    private void UpdateConfigFile() { var cfg = System.Web.Configuration.WebConfigurationManager.OpenWeb ...

  10. Memcached在windows下的安装于使用

    原文链接:http://blog.csdn.net/jjmaiz/article/details/7935672 有一点要注意的是,上文作者没有提及: 将php_memcached.dll放在ext文 ...