本文的环境是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 <% } %>
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();
}
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 }
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 }
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());
来表示二者的绑定关系。
示例程序下载http://files.cnblogs.com/chsword/MyModelBinder.rar