在MVC(Model-View-Controller)架构中,经常会遇到控制器(controller)、动作方法(action methods)、应用程序路由(application routes)和模型(model)等组件。这些组件无疑是任何MVC应用程序中的重要部分,但还有一个在幕后同样重要的组件,那就是模型绑定器(Model Binder)。
在控制器中定义动作方法时,可能会这样写:
public ActionResult EmployeeDetails(int empId) { /* 方法体 */ return View(); }
在上面的方法中,参数empId
的类型是整数,它将从请求中获取id值。可能在查询字符串(querystring)中提供了empId
值,或者作为表单字段的一部分,但重要的是没有将值映射到动作方法参数,也没有提供任何指令来映射特定的表单字段或查询字符串值到动作方法参数。这种映射是由默认的模型绑定器自动完成的。
可以通过下面的图表来演示模型绑定的过程:
当定义动作方法参数时,这些参数的值将由默认模型绑定器自动填充。在模型绑定过程中有两个主要点需要考虑:
例如,如果定义了一个名为empId
的动作方法参数,那么该字段应该存在于任何不同的请求数据位置。如果定义empId
为整数参数,那么提供的值应该是可以转换为整数的。不能例如为empId
提供一个字符串值,否则会抛出运行时异常。
模型绑定器依赖于另一个组件来提供请求值,即值提供者(Value Provider)。值提供者搜索不同的请求源以获取数据,然后将数据提供给模型绑定器,模型绑定器再将数据绑定到动作方法参数。
值提供者搜索数据的不同位置包括:
上述请求数据源是按照顺序搜索的。例如,如果在表单中找到了一个值,那么其他位置就不会搜索该值。模型绑定器按照参数名称搜索上述位置。
值提供者搜索不同的请求位置,并将值提供给模型绑定器。
一个重要的考虑是,尽管可以将任意数量的参数传递给动作方法,如果参数名称与请求数据匹配,那么模型绑定器将用请求中的信息填充它们,但有时这可能很难管理。因此可以定义一个方法,如下所示:
public ActionResult Details(int empId, string name, string department, string address, DateTime dateOfJoining, string status) { return View(); }
上述方法定义是可行的,但不易管理。如果明天在表单中添加了一个新的输入字段,那么就需要在方法中添加参数。同时,从方法签名中,无法清楚地了解传递给方法的实体。
另一种更好的传递数据到动作方法的方式是将所有动作方法参数移动到一个实体中(如果这样的实体尚未存在),并将实体作为参数传递。实体在这里充当数据传输对象。可以将上述所有方法参数封装在一个Employee
类中,如下所示:
public class Employee { int empId { get; set; } string name { get; set; } string department { get; set; } string address { get; set; } DateTime dateOfJoining { get; set; } string status { get; set; } }
public ActionResult Details(Employee emp) { return View(); }
将集合传递到动作方法
模型绑定的一个有用场景是需要将一个集合传递给方法。假设使用的是已经定义的Employee
类:
public class Employee { public int empId { get; set; } public string name { get; set; } public string department { get; set; } public string address { get; set; } public DateTime dateOfJoining { get; set; } public string status { get; set; } public List reportees { get; set; } }
然后,为了允许用户提供Employee
类的信息,包括reportees集合的信息,可以在视图中定义输入字段,如下所示:
@using (Html.BeginForm("Details", "Home")) { /* 表单内容 */ }
上述标记将呈现以下页面:
提交上述表单数据后,如果观察传递给动作方法的Employee
参数,可以看到reportees列表自动填充了。在上面,只是给reportees输入字段赋予了相同的名称,集合就自动用这些输入字段填充了。
如果不得不自己填充集合,那么这不仅是一个繁琐的过程,而且也容易出错。
要自定义默认模型绑定,可以使用Bind属性,它有三个有用的属性:
将采用前面的例子,并填写员工模型对象的不同字段,然后提交表单,看看将上述属性应用于动作方法参数的效果。
填写表单并提交:
所有员工参数属性都默认填充了:
Include
如果将Bind属性的Include属性设置为Name,那么除了Name员工属性之外,其余的请求值都被忽略,只有Name员工属性被默认模型绑定器填充。
Exclude
如果将Bind属性的Exclude属性设置为Name,那么除了Name属性之外,其余属性都被默认模型绑定器填充。
Prefix
可以使用Prefix属性将请求字段与Prefix属性值匹配,而不是方法参数。可以定义一个名为firstName的输入字段,参数仍然会被firstName字段填充,即使方法参数被称为name,通过使用Bind属性的Prefix属性。
@Html.LabelFor(x=>x.name)
public ActionResult Details([Bind(Prefix="firstName")] string name) { return View(); }