Asp.net Mvc Codeplex Preview 5 第三篇 实现Action参数传递繁杂类型

本文的环境是Asp.net Mvc Codeplex Preview 5

 

前文提到我们可以使用 Controller中的UpdateModel来获取 繁杂类型

例如

1 UpdateModel(x, Request.Form.AllKeys);

但是这里有些问题,当我们使用Request.Form.AllKeys时,提交的数据中有非x属性时,就会发生错误

The model of type 'MyModel' does not have a property named 'Name2'.

但是使用

1 UpdateModel(x, new[] {"IDX""Name"});

这种形式,我们又会觉得它太过麻烦。

 

其实Asp.net Mvc为我们提供了一种很简单的传递复杂数据的方式,它类似于Monorail中的DataBinder:

我们完全可以通过以下方式来传递数据。例如

 view:

1     <%using (Html.Form("home""about", FormMethod.Post)) {%>
2     <%=Html.TextBox("my.ID")%>
3     <%=Html.TextBox("my.Name")%>
4     <%=Html.SubmitButton()%>
5     <%%>

 controller:

        [AcceptVerbs("post")]
        
public ActionResult About([ModelBinder(typeof(MyModelBinder))]MyModel my) {
            ViewData[
"Title"=my.Name + my.ID;
            
return View();
        }

 这样我们就可以从my中获取到Post过来的值了,这里的关键在于[ModelBinder(typeof(MyModelBinder))]

 

而 MyModelBinder的实现方法如下

 

 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Globalization;
 5 using System.Linq;
 6 using System.Web.Mvc;
 7 
 8 /// <summary>
 9 /// 这个类是根据Controller.UpdateModel方法更改而成
10 /// </summary>
11 public class MyModelBinder : IModelBinder{
12     #region IModelBinder 成员
13 
14     public object GetValue(ControllerContext controllerContext, string modelName, Type modelType,
15                            ModelStateDictionary modelState){
16         object model = Activator.CreateInstance(modelType); //将做为参数的类实例化了
17         IEnumerable<string> keys = modelType.GetProperties().Select(c => c.Name); //得到该对象的属性的名的字符串数组,这里的结果应该为["ID","Name"]
18         string objectPrefix = modelName; //这个就是,我的对象名叫my则会检查  name="my.ID" name="my.Name"的表单字段
19 
20         PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(model); //对象的属性的集合
21         var dictionary = new Dictionary<string, PropertyDescriptor>();
22         foreach (string str in keys){
23 //遍历属性的字符串集合即["ID","Name"]
24             if (!string.IsNullOrEmpty(str)){
25                 PropertyDescriptor descriptor = properties.Find(str, true);
26                 if (descriptor == null){
27                     throw new ArgumentException(
28                         string.Format(CultureInfo.CurrentUICulture, "无此属性{0},{1}"new object[]{model.GetType().FullName, str}),
29                         "modelName");
30                 }
31                 string str3 = string.IsNullOrEmpty(objectPrefix) ? str : (objectPrefix + "." + str); //将对象名与属性名拼接,如my.ID
32                 dictionary[str3] = descriptor;
33             }
34         }
35         foreach (var pair in dictionary){
36             string key = pair.Key;
37             PropertyDescriptor descriptor2 = pair.Value;
38             object obj2 = ModelBinders.GetBinder(descriptor2.PropertyType).GetValue(controllerContext, key,
39                                                                                     descriptor2.PropertyType, modelState);
40             if (obj2 != null){
41                 try{
42                     descriptor2.SetValue(model, obj2); //设置属性的值
43                     continue;
44                 }
45                 catch{
46                     //如果有使用验证Helepr则会显示在Html.ValidationSummary中
47                     string errorMessage = string.Format(CultureInfo.CurrentCulture, "验证失败{0}:{1}"new[]{obj2, descriptor2.Name});
48                     string attemptedValue = Convert.ToString(obj2, CultureInfo.CurrentCulture);
49                     modelState.AddModelError(key, attemptedValue, errorMessage);
50                     continue;
51                 }
52             }
53         }
54         return model; //最后 返回这个我们设置完属性的对象
55     }
56 
57     #endregion
58 }

 

这样我们就实现了 用Action的参数传递复杂类型。

 

当然,如果你连[ModelBinder(typeof(MyModelBinder))]都不想写了,想直接来以下写法,

1         [AcceptVerbs("post")]
2         public ActionResult About(MyModel my) {
3             ViewData["Title"=my.Name + my.ID;
4             return View();
5         }

这个也是可以的不过你要在Application_Start中添加

ModelBinders.Binders.Add(typeof (MyModel), new MyModelBinder());


来表示二者的绑定关系。

 

多谢Leven's Bloglulu Studio

 

 

示例程序下载http://files.cnblogs.com/chsword/MyModelBinder.rar

 

posted @ 2008-08-31 14:27 重典 阅读(13264) 评论(55) 编辑 收藏

 回复 引用 查看   
#1楼2008-08-31 14:31 | Leven      
坐下非典的沙发......
 回复 引用 查看   
#2楼2008-08-31 14:33 | 逸之羊      
楼主真是强人啊
 回复 引用 查看   
#3楼2008-08-31 14:36 | 紫色永恒      
顶非典
 回复 引用 查看   
#4楼2008-08-31 15:02 | Q.Lee.lulu      
好文!!
牛X啊

 回复 引用 查看   
#5楼2008-08-31 15:02 | 侯垒      
路过,也顶。
 回复 引用 查看   
#6楼[楼主]2008-08-31 15:09 | 重典      
@Leven
。。。神鱼来访。。。

 回复 引用 查看   
#7楼[楼主]2008-08-31 15:09 | 重典      
@逸之羊
可爱的羊吗?

 回复 引用 查看   
#8楼[楼主]2008-08-31 15:10 | 重典      
@紫色永恒
哪位哪位,报上名来

 回复 引用 查看   
#9楼[楼主]2008-08-31 15:10 | 重典      
@Q.Lee.lulu
路路也来访了,真是有幸

 回复 引用 查看   
#10楼[楼主]2008-08-31 15:10 | 重典      
@侯垒
多谢多谢

 回复 引用 查看   
#11楼2008-08-31 15:21 | 王文明      
新技术更新太快了,楼主的三篇我都仔细看了,谢谢楼主。不过再我看来,待微软的这个东西大体定形了,我再仔细研究比较好,现再可以多学一些成熟的东西比较好!
 回复 引用 查看   
#12楼[楼主]2008-08-31 15:25 | 重典      
@王文明

呵呵,谢谢观注,其实微软的东西很多,应该选一个方面深入一下的

 回复 引用   
#13楼2008-08-31 16:21 | xe[未注册用户]
good
 回复 引用 查看   
#14楼[楼主]2008-08-31 19:38 | 重典      
@xe
谢谢支持

 回复 引用   
#15楼2008-09-01 11:36 | 净幻小乖![未注册用户]
非典厉害咯
 回复 引用 查看   
#16楼[楼主]2008-09-01 11:43 | 重典      
@净幻小乖!
过奖啥

 回复 引用 查看   
#17楼2008-09-01 13:58 | 勇敢的兽      
Action的参数是一些搜索条件,如条件1 条件2,那么不需要建立Model吧,那么是不是只能用
=======================================
ViewData["CustomerID"] = this.ReadFromRequest("CustomerID") != null ? this.ReadFromRequest("CustomerID") : "";
ViewData["CustomerName"] = this.ReadFromRequest("CustomerName") != null ? this.ReadFromRequest("CustomerName") : "";


//DataSet for List
var customerSet = from c in data.Customer
where c.CustomerName.IndexOf(ViewData["CustomerID"].ToString()) != -1 &&
c.CustomerName.IndexOf(ViewData["CustomerName"].ToString()) != -1
select c;

========================================

这样的方式获取呢?


总觉得
ViewData["CustomerName"] = this.ReadFromRequest("CustomerName") != null ? this.ReadFromRequest("CustomerName") : "";
这样写不太好

 回复 引用 查看   
#18楼2008-09-01 14:01 | 勇敢的兽      
临时Model ? 跟Controls的程序块放在一起??
 回复 引用 查看   
#19楼[楼主]2008-09-01 14:02 | 重典      
直接ActionReault Action(int CustomerID)就能获取
 回复 引用 查看   
#20楼[楼主]2008-09-01 14:03 | 重典      
@勇敢的兽
那样不行,没有临时的

 回复 引用 查看   
#21楼2008-09-01 14:18 | 勇敢的兽      
条件很多啊。。。。
而且
Customer自身的属性是一种搜索(ID,Name)
Customer相关的订单是一种搜索(订单号,订单商品)

也就是说这个Action(Customer/List)有两个Form, 两个search button ,都是Post到List,该如何处理呢?

 回复 引用 查看   
#22楼[楼主]2008-09-01 14:22 | 重典      
如果多的话,还是建一个类吧
 回复 引用 查看   
#23楼2008-09-01 19:33 | 勇敢的兽      
Preview5 好像没有ReadFromRequest了

一定需要建立model或者把post的变量作为action的参数吗??
还是用HttpRequestBase的Request["XXX"]??

 回复 引用 查看   
#24楼[楼主]2008-09-01 19:59 | 重典      
@勇敢的兽
Request.Form也可以,其实更可以按这篇文章中这样传递

 回复 引用 查看   
#25楼2008-09-01 20:06 | 勇敢的兽      
通常一个action会接收一个form中的数据进行处理,那么可以用action的参数来获取form中的数据,然后这个action中会处理多个form提交上来的数据,这些form中的数据又完全没有关系的,那么最好的方法就是建立model?

即便只其中的部分form中只有1个变量也要建立model吗?

复杂的form倒是如重典大哥所说,建立一个model(其实总觉得不太好,因为我总觉得model说白了就是针对于数据库中的表的,或者是一类对象的)

 回复 引用 查看   
#26楼[楼主]2008-09-01 20:13 | 重典      
其实一切的目的就是少写代码+结构清析,没有一定之规,也不一定要建Model

怎么自己感觉好怎么来

 回复 引用 查看   
#27楼2008-09-01 20:18 | 勇敢的兽      
嘻嘻,看来这个问题钻牛角尖了,正在看你的System.Web.Routing相关的文章,您辛苦了
 回复 引用 查看   
#28楼[楼主]2008-09-01 20:22 | 重典      
@勇敢的兽
是的,不用太刻意,顺其自然就好 了这个和数据库不一样,没有什么太大的性能差别

 回复 引用 查看   
#29楼2008-09-01 20:37 | 勇敢的兽      


=====================================
StructureMap configuration failures:
Error: 104
Source: Registry: Commerce.MVC.Web.StorefrontRegistry,Commerce.MVC.Web
Type Instance '21a66a1f-1559-4ae4-9d7b-43036e0604c9' (Configured '') cannot be plugged into type Commerce.MVC.Services.IMailerService, Commerce.MVC.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
======================================

这个错误是什么意思啊....

 回复 引用 查看   
#30楼[楼主]2008-09-01 20:43 | 重典      
Commerce.MVC.Services这个类型的DLL或这个类型本身不存在

 回复 引用 查看   
#31楼[楼主]2008-09-01 20:43 | 重典      
@勇敢的兽
Commerce.MVC.Services这个类型的DLL或这个类型本身不存在

 回复 引用 查看   
#32楼2008-09-01 20:46 | 勇敢的兽      
怪事了,编译都是通过的,一调试就出问题了

我看的是Commerce,传说中已经用于商用的ASP.NET MVC 项目

 回复 引用 查看   
#33楼[楼主]2008-09-01 20:52 | 重典      
@勇敢的兽
这是Ioc是执行时加载的

 回复 引用   
#34楼2008-09-02 17:00 | kical[未注册用户]
楼主太强了,问你一个问题啊
在MVC下的用户控件,这个用户里面有提交按钮,我点击这个提交按钮的时候,却返回用户控件的HTML,而外面加载这个用户控件的ASPX页的HTML全部都没有了。
我怎样让MVC的用户控件和常规的用户控件类似呢?

 回复 引用 查看   
#35楼[楼主]2008-09-02 17:09 | 重典      
@kical
您所指的用户控件是传统的Ascx吗?

这个要具体看看才知道的

 回复 引用   
#36楼2008-09-03 10:35 | kical[未注册用户]
在用户控件里面有个按钮,HTML如下

<%using (Html.Form("ArStatus", "Add"))
{ %>
<%=ViewData["temp"]%>
<%=Html.TextBox("test", ViewData["test"])%>
<%=Html.SubmitButton("save", "Save", new { Class="commonButton"})%>
<%} %>

控制层代码如下

public class ArStatusController : Controller
{
public ActionResult Index()
{
// Add action logic here
ViewData["temp"] = DateTime.Now;
return View("ArStatus");
}
public ActionResult Add(string test)
{
ViewData["temp"] = DateTime.Now;
ViewData["test"] = "hello:"+test;
retutn View("ArStatus");

}
}

接着我在一个ASPX代码中,加载这个控件,如下

...

<%Html.RenderAction<Armonitor.Controllers.ArStatusController>(ar => ar.Index()); %>

...

一开始加载控件能够在ASPX正确显示出来(IE客户端HTML包含,ASPX和用户控件),现在问题就是,我点击那个SubmitButton的时候,页面就跳转到ArStatus/Add,同时IE客户端的HTML代码只包含用户控件的,ASPX就不再有了,我该如果解决这个问题?让MVC的用户控件和常规的用户控件实现一样的功能。

感谢你回答,我把代码贴上来
--------------------------------------------------------------------------------
kical

 回复 引用 查看   
#37楼[楼主]2008-09-03 10:43 | 重典      
@kical
正常就是这样的,因为在这里用户控件与传统的有些区别
它可以独立显示,只是用扩展名来标注它是页面的一部分而已,本质与之是一样的,如果你想显示完全的

public ActionResult Add(string test)
{
ViewData["temp"] = DateTime.Now;
ViewData["test"] = "hello:"+test;
retutn RenderToAction("您刚才引用了用户控件的那页");
}
或者
RedirectToReferrer
这个是RedirectToReferrer的扩展,有兴趣可以看一下
http://www.cnblogs.com/chsword/archive/2008/06/01/AspnetExtRenderRedirect.html

 回复 引用   
#38楼2008-09-03 11:38 | kical[未注册用户]
用你的两种方法都用户过了,现在IE可以包含用户控件和ASPX页面的HTML数据了。
但是重定向之后ViewData数据却丢失了,也就是
public ActionResult Add(string test)
{
ViewData["temp"] = DateTime.Now;//ViewData[数据丢失
ViewData["test"] = "hello:"+test; //ViewData数据丢失
retutn RenderToAction("您刚才引用了用户控件的那页");
}
这个有办法解决吗?非常感谢,这个问题困扰我很长时间了
(其实是返回原来的页面,原来的页面有这样的代码
Html.RenderAction<Armonitor.Controllers.ArStatusController>(ar => ar.Index())
这样就再一次访问用户控件的Index方法了)

 回复 引用 查看   
#39楼[楼主]2008-09-03 11:48 | 重典      
可以的,就在Add中重新实现 引用了用户控件那页的Viewdata 并return View(“引用用户控件的那个的View”)
 回复 引用 查看   
#40楼[楼主]2008-09-03 11:48 | 重典      
@kical
可以的,就在Add中重新实现 引用了用户控件那页的Viewdata 并return View(“引用用户控件的那个的View”)

 回复 引用   
#41楼2008-09-03 12:08 | kical[未注册用户]
我试了一下,Add中无法访问引用页的ViewState阿,能简单代码提示一下吗?
 回复 引用 查看   
#42楼[楼主]2008-09-03 12:17 | 重典      
不访问,
是说再写一遍
将那个Action中的代码copy过来

 回复 引用   
#43楼2008-09-03 12:25 | kical[未注册用户]
COPY过来,用户控件里面的获取不到
<%=ViewData["temp"]%> //获取不到temp
<%=Html.TextBox("test", ViewData["test"])%> //获取不到test
而且这样还有一个问题,就是Add函数有一个参数public ActionResult Add(string test),我要进行处理ViewData["test"] = "hello:"+test;
多谢牛人啊,麻烦你了,还有什么办法解决吗?

 回复 引用 查看   
#44楼[楼主]2008-09-03 12:31 | 重典      
正常是这样的流程的
index是单独的页而不是控件,然后原来的引用它的页可以做成Master
点提交,再在Add中加
这样就可以显示数据了
Add(){
return index();
}

 回复 引用   
#45楼2008-09-03 13:34 | kical[未注册用户]
搞定啦,谢谢
 回复 引用 查看   
#46楼[楼主]2008-09-03 13:36 | 重典      
@kical
^^

 回复 引用 查看   
#47楼2008-09-18 16:52 | triplel      
这个就实在是很强了
 回复 引用 查看   
#48楼[楼主]2008-09-18 16:56 | 重典      
@triplel
还没有达到MR中DataBinder的智能程度,不是太好用

 回复 引用 查看   
#49楼2008-09-19 17:28 | 李路平      
不错的文章!~学习了
 回复 引用 查看   
#50楼2008-09-19 17:37 | 李路平      
有个问题~!这样不是更简单?
<%using (Html.Form("home", "Login", FormMethod.Post)) {%>
<%=Html.TextBox("username")%>
<%=Html.TextBox("password")%>
<%=Html.SubmitButton()%>
<%} %>

public ActionResult Login(string username, string passWord)
{
if (Request.HttpMethod == "POST")
{
ViewData["Title"] =username + passWord;
return View();
}
}

 回复 引用 查看   
#51楼[楼主]2008-09-19 17:44 | 重典      
如果你要提交注册信息
信息是一个表,
如:http://register.ourgame.com/regist/index.html
如果参数的话
Register(string username, string passWord,Email,int Province,string name,string 身份证号码,bool 性别,DateTime 出生日期,string 国家,string 附加码....)
这样岂不累死了

如果正好有个类User,有这些信息,
Register(User u)不是更方便嘛

 回复 引用 查看   
#52楼2008-11-05 10:02 | 勇敢的兽      
Asp.net MVC Beta 中 IModelBinder接口没有GetValue()方法了
 回复 引用 查看   
#53楼2008-11-05 10:52 | 勇敢的兽      
Beta中,直接用

public ActionResult About(MyModel my) {} 就可以了

 回复 引用 查看   
#54楼[楼主]2008-11-08 22:36 | 重典      
@勇敢的兽
是的,很爽了

 回复 引用   
#55楼2008-12-21 00:03 | cokkiy[未注册用户]
好文章,真想用MVC来做一个项目