预备知识:扩展方法(Extension Method);Asp.net MVC
一般的做法
以修改一篇blog为例,当我们再输入框中输入了修改的内容之后,通过点击提交按钮将新的blog内容提交到服务器端,如图所示,我们可以修改blog的title,body和timestamp三项:
图 1
我们可能写出这样的代码:
[ControllerAction] public void Save(int id) { BlogDataContext dbBlog = new BlogDataContext(); Post post = dbBlog.Posts.Single(p => p.ID == id);
post.Body = Request.Form["Body"]; post.Title = Request.Form["Title"]; post.Timestamp = DateTime.Parse(Request.Form["Timestamp"]); dbBlog.SubmitChanges(); RedirectToAction(new { action = "Detail", id = post.ID }); }
这段代码显示了controller从view的Request中读取内容,然后更新model。在新的Asp.net模型中,view的内容都放在Request.Form对象中,该对象是一个HttpValueCollection集合。
图 2
仔细看它里面存储的值,实际上就是我们在以前看到的Request.QueryString,只是现在我们不用在Url中显示地用?来写出,而是由Asp.net MVC框架帮我们生成,它会自动读取我们在form中定义的有些元素及其中所包含的内容。下面是一个form的例子,红色标注的地方可以先忽略:
图 3
BindingHelperExtension.UpdateFrom让更新变得更加容易
回到更新blog内容的问题上来,下面的代码可以完成更新任务:
post.Body = Request.Form["Body"];
post.Title = Request.Form["Title"];
post.Timestamp = DateTime.Parse(Request.Form["Timestamp"]);
但是这样一个属性一个属性地修改Model的值很繁琐,而且中间涉及到类型转换。比如上面的DateTime类型的属性Timestamp,严格的说我们应该在更新它的值之前坐类型判断,空值判断等等操作。当属性超过10个甚至更多时,更新属性的代码加上类型转换,错误处理等等,代码的数量就比较打了。那么有没有更加简便的做法呢?实际上在MvcToolkit.dll这个程序集中为我们提供了一个简化操作的类,它就是UrlHelper,是一个扩展方法,可以附加到任何的Object类型的对象上。
图 4
添加对MvcToolkit.dll的引用,并引入名字空间
using System.Web.Mvc.BindingHelpers;
之后我们就可以使用BindingHelpExtension类了。
[ControllerAction] public void Save(int id) { BlogDataContext dbBlog = new BlogDataContext(); Post post = dbBlog.Posts.Single(p => p.ID == id);
post.UpdateFrom(Request.Form);//post.Body = Request.Form["Body"]; //post.Title = Request.Form["Title"]; //post.Timestamp = DateTime.Parse(Request.Form["Timestamp"]);dbBlog.SubmitChanges(); RedirectToAction(new { action = "Detail", id = post.ID }); }
这样修改一个对象的属性是不是很简单呢?毫无疑问,而且这样还带来了另外一个好处,如果这个时候Post的属性变了(属性名或属性个数),我们不需要修改Controller的代码,需要修改的只是View部分。
使用UpdateFrom的前提
UpdateFrom固然很好用,可是需要配合Form的Action来完成,而且它对于view中元素的定义有一定的要求。
Url.Action
先来看看Action。我们为Form添加Action,注意method属性是post的,因为是提交给服务器。
<form action="<%= Url.Action(new {action="Save",id=ViewData.ID}) %>" method="post">
这里action的语法有一点怪异,实际上它生成后的Url是这样的:
<form action="/MVCStudy/Blog/Save/10" method="post">
这里的Url实际上ViewPage的一个属性,而该属性又是UrlHelper的一个实例,所以这里的Url.Action实际上System.Web.Mvc.UrlHelper类的方法Action,该类存在于System.Web.Extension.dll程序集中。
图 5
至于Url.Action方法生成Url的原理,可以参考Scott的文章“ASP.NET MVC Framework's Routing Engine”
HtmlName——指定HtmlName,updatefrom才能生效。
这里的HtmlName是指在使用HtmlHelper类来生成html元素时所需要指定的相应的html元素的名字:
图 6
比如:
图 7
这里虽然生成id和name两个属性,但是Asp.net MVC使用的"name"。生成这一元素的代码:
<p class="body"> <%--<%=Html.TextArea("bodyOfBlog",ViewData.Body) %>--%> <%=Html.TextArea("Body",ViewData.Body) %> </p>
注意这里的HtmlName一定要与对应的model的属性名一致,否则无法完成更新。上述代码中注释掉的代码行就是这样一个例子。正确的HtmlName设定,请参考本文图3。
总结
使用UpdateFrom可以为我们带来很多便利,例如类型转换、空值检查之类,同时它也使得代码更加容易维护,因为在controller中不需要理会model的具体的属性,充分发挥了Data Banding的作用。让框架为我们做更多的事,那么我们就可以更省事。
如果你的UpdateFrom不起作用,那么首先应该检查一下Form的action以及HtmlName的设定是否正确.