MVC 数据绑定
在做Asp.Net MVC项目中,都知道View负责页面展示数据或者提供页面收集数据,而所展示的数据或者收集的数据都是从Controller的Action中获取或提交到Controller的Action。
这里的数据,可能是基础类型,或者是Model,或者是Model的部分内容,或者是集合比如List或Dictionary。
数据从View传递到Controller的Action时,有几种方式,RouteData(url中的路由数据),QueryString(http get的查询参数如?page=2),Forms(表单post的数据), 或者ajax交互的json数据。
而在Controller的action中,常常希望获得这些View传递的数据,并且最好能绑定到Action希望的类型,这些类型可能是Action的基础类型参数,或者需要更新的Model,或者只更新Model的部分内容,或者是一些集合的结构如List或Dictionary。
古董方法:
[AcceptVerbs(HttpVerbs.Post)]
publicActionResultCreate()
{
Reciperecipe = newRecipe();
recipe.Name = Request.Form["Name"];
// ...
returnView();
}
前进一步:
publicActionResultCreate(FormCollectionvalues)
{
Reciperecipe = newRecipe();
recipe.Name = values["Name"];
// ...
returnView();
}
神奇的DefaultModelBinder:
Asp.Net Mvc内建功能(DefaultModelBinder)可以实现简单类型、复杂类型、集合类型,以及字典类型的自动绑定。
1. 简单类型
这里,我们将下面这个Book类称为简单类型:
public class Book
{
public int BookId { get; set; }
public string BookName { get; set; }
public string Author { get; set; }
public DateTime PublishedDate { get; set; }
}
假设现在需要实现添加Book的功能,那么在BookController中,会定义如下的Action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Book book) {
//TO DO
//Insert book into Database
return RedirectToAction("Index");
}
现在的问题便是,在View中如何命名TextBox来达到自动绑定,如下:
<div>
<%using (Html.BeginForm("Create", "Book")) { %>
<div>
Book Name: <%=Html.TextBox("BookName")%>
</div>
<div>
Author: <%=Html.TextBox("Author")%>
</div>
<div>
Published Date: <%=Html.TextBox("PublishedDate")%>
</div>
<div>
<input type="submit" id="submit" name="submit" value="submit" />
</div>
<%} %>
</div>
注意TextBox的name必须是对应绑定类型的PropertyName(不区分大小写)。 这样,页面表单submit后,我们便可以在BookController的“Create” Action中得到自动绑定的book对象。这里,Asp.Net Mvc还支持在TextBox的name中加上变量名称前缀的情形:
<div>
<%using (Html.BeginForm("Create", "Book")) { %>
<div>
Book Name: <%=Html.TextBox("book.BookName")%>
</div>
<div>
Author: <%=Html.TextBox("book.Author")%>
</div>
<div>
Published Date: <%=Html.TextBox("book.PublishedDate")%>
</div>
<div>
<input type="submit" id="submit" name="submit" value="submit" />
</div>
<%} %>
</div>
需要注意的是:
1)前缀"book"必须与Action中接收的Book参数名一致
2)如果加了前缀,那么所有的都要加
2. 复杂类型
现在对Book类作些许改动,并引入Author类:
public class Book
{
public int BookId { get; set; }
public string BookName { get; set; }
public Author Author { get; set; }
public DateTime PublishedDate { get; set; }
}
public class Author {
public int AuthorId { get; set; }
public string AuthorName { get; set; }
public string Nation { get; set; }
}
这里,将改动后的Book类称为复杂类。这时,Book类多了一个对Author类的引用。现在,保持BookController中的"Create" Action不变,来看View中的TextBox改如何命名以实现Book类型的自动绑定:
<div>
<%using (Html.BeginForm("Create", "Book")) { %>
<div>
Book Name: <%=Html.TextBox("BookName")%>
</div>
<div>
Published Date: <%=Html.TextBox("PublishedDate")%>
</div>
<div>
Author's Name: <%=Html.TextBox("Author.AuthorName")%>
</div>
<div>
Author's Nation: <%=Html.TextBox("Author.Nation")%>
</div>
<div>
<input type="submit" id="submit" name="submit" value="submit" />
</div>
<%} %>
</div>
OK,测试通过,想必你也知道命名规则了,要绑定Book类型中的Author类型,必须加上"Author."的前缀。
如果你喜欢,你还可以在所有TextBox名称前面再加"book."的前缀。
3. 集合类型
为避免问题复杂化,我们用回原来的简单Book类型:
public class Book
{
public int BookId { get; set; }
public string BookName { get; set; }
public string Author { get; set; }
public DateTime PublishedDate { get; set; }
}
现在,把BookController的"Create" Action改为接收IList<Book>的参数:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(IList<Book> books) {
//TO DO
//Insert book into Database
return RedirectToAction("Index");
}
然后,在View中运用以下命名规则,以自动绑定IList<book>类型,
<div>
<%using (Html.BeginForm("Create", "Book")) { %>
<div>
Book Name: <%=Html.TextBox("books[0].BookName")%>
</div>
<div>
Published Date: <%=Html.TextBox("books[0].PublishedDate")%>
</div>
<div>
Author's Name: <%=Html.TextBox("books[0].Author")%>
</div>
<div>
Book Name: <%=Html.TextBox("books[1].BookName")%>
</div>
<div>
Published Date: <%=Html.TextBox("books[1].PublishedDate")%>
</div>
<div>
Author's Name: <%=Html.TextBox("books[1].Author")%>
</div>
<div>
<input type="submit" id="submit" name="submit" value="submit" />
</div>
<%} %>
</div>
可以看到如下规则:Action的变量名"books"加上中括号和索引作为前缀,索引必须从0开始,并且必须连续。
通过此命名规则,可以绑定任意集合类型:IList<Book>, ICollection<Book>, IEnumerable<Book>, List<Book>, Book[]等。
4. 字典类型
仍以简单Book类型为例,现在将"Create" Action改为接收IDictionary<Book>类型,
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(IDictionary<string, Book> books) {
//TO DO
//Insert book into Database
return RedirectToAction("Index");
}
相应的,View中的命名如下:
<div>
<%using (Html.BeginForm("Create", "Book")) { %>
<div>
Book SN: <%=Html.TextBox("books[0].Key") %>
</div>
<div>
Book Name: <%=Html.TextBox("books[0].Value.BookName")%>
</div>
<div>
Published Date: <%=Html.TextBox("books[0].Value.PublishedDate")%>
</div>
<div>
Author's Name: <%=Html.TextBox("books[0].Value.Author")%>
</div>
<div>
Book SN: <%=Html.TextBox("books[1].Key") %>
</div>
<div>
Book Name: <%=Html.TextBox("books[1].Value.BookName")%>
</div>
<div>
Published Date: <%=Html.TextBox("books[1].Value.PublishedDate")%>
</div>
<div>
Author's Name: <%=Html.TextBox("books[1].Value.Author")%>
</div>
<div>
<input type="submit" id="submit" name="submit" value="submit" />
</div>
<%} %>
</div>
可以看出,对于IDictioinary<Book>类型,在命名规则上,相比于集合类型,多了"Key"和"Value”的字眼。另,此规则也适用于Action参数类型为Dictionary<Book>的情形。
简单类型、复杂类型、集合类型,以及字典类型 还能混合着用,而且绑定的数据来源也不局限于Form提交的表达数据,还可以是RouteData(url中的路由数据),QueryString(http get的查询参数如?page=2),或者ajax交互的json数据。
所以,只要我们遵循一定的命名规则,灵活的运用DefaultModelBinder 就可以轻松实现各种类型的自动绑定了。
BindAttribute:
DefaultModelBinder 已经很强大了,而在有些时候BindAttribute则会为你锦上添花。
BindAttribute中有三个重要的成员:string Exclude, string Include, string Prefix。
"Exclude"用于指定要排除的Property,"Include"用于指定包含在内的Property,"Prefix"用于指定前缀。
看个例子,
Book类:
Create Action:
对应的View:
先说,"Prefix"吧,默认情况下,DefaultModelBinder可以自动识别以Action的参数名命名的前缀,也就是说,在上述View中,给每个TextBox的名称都加一个"book“前缀,不需要在做任何改动,在Action中仍可得到Book的实例。
加上前缀”book"的View:
这时,如果我们加的前缀不是"book",那么就需要BindAttribute的协助了,
假设,我们加上一个"b"的前缀:
那么,为了在Action中得到相应的Book实例,需要在Action的Book参数上应用BindAttribute:
现在来看"Exclude"和"Include",其实这两个东西一次应用一个就可以了。现在我希望Binding的时候将"BookName"和"Author"都排除(当然,这里这样做没什么意义)。出于简化问题的考虑,View去掉TextBox名称的前缀:
然后,将Action改为:
默认情况下,多个Property用逗号隔开。
BindAttribute出了可以应用在Action的参数上外,还可以应用在Model类定义中:
如果在Model类定义中,和在Action的参数上都应用了BindAttribute,那么则会取两者的交集。个人认为,还是应尽量避免它们打架为妙。
所以,BindAttribute 对于前缀的使用及部分Model的绑定很有用。
TryUpdateModel/UpdateModel
Mvc中经常遇到的一个典型用例是View通过From或者Ajax提交数据更新相应的Model,或者更新Model的部分内容,并且需要检查提交的数据对于Model的数据约束是否有效。
这个时候TryUpdateModel就有用武之地了,它可以设置需要更新模型属性列表的(白名单)或要排除属性的列表(黑名单) 先看更新相关模型的所有属性:
publicActionResult Edit(int id, FormCollection collection)
{
var oldData = _r.GetSingleData(id);
if(TryUpdateModel(oldData, collection.AllKeys))
{
_r.Save();
}
}
更新除ID,Name外的Model属性:
publicActionResult Edit(Guid id, FormCollection collection)
{
var oldData = _r.GetSingleData(id);
if(TryUpdateModel(oldData,"", collection.AllKeys,newstring[]{"ID","Name"}))
{
_r.Save();
}
}
publicActionResult Save()
{
Customer customer =newCustomer();
try {
UpdateModel(customer,new[] {"Name","Email",
"Phone","Deposit"});
return RedirectToAction("...");
}
catch(InvalidOperationException)
{
returnView(customer);
}
}
UpdateModel 和TryUpdateModel 有许多重载形式以供多种灵活的运用,而且它还将自动检查模型绑定数据是否有错误,如果有错误将自动添加到ModelState 并在页面作出提示。真是个好用的宝贝。
自定义ModelBinder
MVC 数据绑定的更多相关文章
- Spring MVC—数据绑定机制,数据转换,数据格式化配置,数据校验
Spring MVC数据绑定机制 数据转换 Spring MVC处理JSON 数据格式化配置使用 数据校验 数据校验 Spring MVC数据绑定机制 Spring MVC解析JSON格式的数据: 步 ...
- spring mvc 数据绑定
1.spring mvc 默认提供的数据绑定类 private List<HandlerMethodArgumentResolver> getDefaultArgumentResolver ...
- Spring MVC 数据绑定 (四)
完整的项目案例: springmvc.zip 目录 实例 项目结构路径: 一.配置web.xml <?xml version="1.0" encoding="UTF ...
- Spring MVC 数据绑定流程分析
1. 数据绑定流程原理★ ① Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 Data ...
- Spring MVC数据绑定(二)
之前学习了SpringMVC数据绑定的基本知识和简单数据绑定以及POJO类型数据的绑定.接下来总结剩下的一些数据类型的绑定 1. 绑定包装POJO 所谓的包装POJO,就是在一个POJO中包含另一个简 ...
- Spring MVC数据绑定(一)
1.数据绑定介绍 在执行程序时,Spring MVC会根据客户端请求参数的不同,将请求消息中的信息以一定的方式转换并绑定到控制器类的方法参数中.这种将请求消息数据与后台方法参数建立连接的过程就是Spr ...
- Spring MVC 数据绑定(四)
Spring支持多种形式的类型绑定,包括: 1.基本数据类型.String和String[] 2.简单对象类型 3.List类型 4.Set类型 5.Map类型 6.复合数据类型 接下 ...
- spring mvc 数据绑定总结
spring mvc 做web开发时,经常会不知道如何合适绑定页面数据.用惯struts2的朋友更认为spring mvc 绑定数据不如struts2方便(本人最开始也是这么认为),经过一段时间的应用 ...
- Spring MVC数据绑定入门总结
1.基本类型 基本类型参数不可为空 正例:http://localhost:8080/demo/he?id=2 反例:http://localhost:8080/demo/he?id=(报400错误) ...
随机推荐
- Github朝花夕拾
删除fork的项目 下载指定revision的repository 通过git log查看提交历史,最好是GUI查看 然后执行命令git reset –hard <sha1> 同步到最 ...
- sql 根据字段查询不同表
SELECT snFundType,snBusinessType, THEN (select vcPaySerialNo from aa where vcSerialNo=a.vcRelationSe ...
- Vue.js 指南-基础
Installation 可以使用的方式: script标签方式加载vue.js cdn https://unpkg.com/vue@2.0.5/dist/vue.js npm Introductio ...
- openstack私有云布署实践【14.2 登录页dashboard-controller(办公网环境)】
这一小节基本配置相同,但留意以下紫色部份的配置,当初为了管理方便,我们让办公网openstack的dashboard的登录桥接了科兴的dashboard,由此统一dashboard界面的登录地址 ...
- L3-007. 天梯地图
L3-007. 天梯地图 题目链接:https://www.patest.cn/contests/gplt/L3-007 Dijstra 这题是Dijstra的变形,麻烦的是两种最短路的相同距离时的选 ...
- POJ 2234 Matches Game(取火柴博弈1)
传送门 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> ...
- OpenCV2.x自学笔记——自适应阈值
adaptiveThreshold(src,dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, do ...
- OMCS使用技巧 -- 摄像头及其动态能力
在开发类似视频聊天的应用时,我们经常需要获取摄像头的相关信息:而在进行视频聊天时,我们可能还希望有一些动态的能力.比如,在不中断视频聊天的情况下,切换一个摄像头.或者修改摄像头采集的分辨率或编码质量等 ...
- LeetCode OJ 64. Minimum Path Sum
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which ...
- 2016NEFU集训第n+3场 G - Tanya and Toys
Description In Berland recently a new collection of toys went on sale. This collection consists of 1 ...