导航

MVC 表单验证的几种方式:

Posted on 2012-04-21 17:29  龚振  阅读(1026)  评论(0编辑  收藏  举报

前提:数据建模是通过ADO.Net Entity 生成。原型如下:

 1    [EdmEntityTypeAttribute(NamespaceName="GoodsModel", Name="Customer")]
 2     [Serializable()]
 3     [DataContractAttribute(IsReference=true)]
 4     public partial class Customer : EntityObject
 5     {
 6         #region Factory Method
 7     
 8         /// <summary>
 9         /// Create a new Customer object.
10         /// </summary>
11         /// <param name="code">Initial value of the Code property.</param>
12         public static Customer CreateCustomer(global::System.Int32 code)
13         {
14             Customer customer = new Customer();
15             customer.Code = code;
16             return customer;
17         }
18 
19         #endregion
20         #region Primitive Properties
21     
22         /// <summary>
23         /// No Metadata Documentation available.
24         /// </summary>
25         [EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
26         [DataMemberAttribute()]    
27         public global::System.Int32 Code
28         {
29             get
30             {
31                 return _Code;
32             }
33             set
34             {
35                 if (_Code != value)
36                 {
37                     OnCodeChanging(value);
38                     ReportPropertyChanging("Code");
39                     _Code = StructuralObject.SetValidValue(value);
40                     ReportPropertyChanged("Code");
41                     OnCodeChanged();
42                 }
43             }
44         }
45         private global::System.Int32 _Code;
46         partial void OnCodeChanging(global::System.Int32 value);
47         partial void OnCodeChanged();
48     
49         /// <summary>
50         /// No Metadata Documentation available.
51         /// </summary>
52         [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
53         [DataMemberAttribute()]    
54         [ValidateLength(200)] 
55         public global::System.String Company
56         {
57             get
58             {
59                 return _Company;
60             }
61             set
62             {
63                 OnCompanyChanging(value);
64                 ReportPropertyChanging("Company");
65                 _Company = StructuralObject.SetValidValue(value, true);
66                 ReportPropertyChanged("Company");
67                 OnCompanyChanged();
68             }
69         }
  • 这种情况下最直接,也就是最容易想到的是,在控制器中直接调用ModelState.AddModelError().         
 1         [HttpPost]
 2         public ActionResult Create(Customer customer)
 3         {
 4             if (customer.Company.Length > ComanyMaxLength)
 5                 ModelState.AddModelError("Company""Company must be at most ComanyMaxLength characters long");
 6 
 7             if (ModelState.IsValid)
 8             {
 9                 return View(customer);
10             }
11             else
12             {
13                 new CustomerModels().CreateCustomer(customer);
14                 return RedirectToAction("Details""Customer"new { ID = customer.Code });
15             }
16         }

这样写简单,而且我们还有更简单的办法就是

  • 我们可以直接给MVC 中model filed 加上ValidateAttribute,下面是模板生成的ChangePasswordModel 中的代码。
1         [Required]
2         [ValidatePasswordLength]
3         [DataType(DataType.Password)]
4         [DisplayName("New password")]
5         public string NewPassword { getset; }

给所有需要的字段加上CustomerAttribute. 但问题来了,数据的里面有那么多的表,每个字段的长度也不一样,一一对应起来肯定麻烦,也容易出错。而且ADO.Net Entity 生成的Model 要加CustomerAttribute 也不是那么容易,同样工作量大,耦合性强。那有没有更好的方法呢,

  • 那就是利用ModelBinder 扩展,通过MetadatWorkspace 读取csdl的元数据进行验证。
 1     public class ValidationModelBinder : DefaultModelBinder
 2     {
 3         protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)
 4         {
 5             base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
 6 
 7             if(value==nullreturn;
 8             var edmWorkspace = new MetadataWorkspace(new[] { "res://*/Entity.Goods.csdl" }, new Assembly[] { Assembly.GetAssembly(typeof(Customer)) });
 9             foreach (var edmType in edmWorkspace.GetItems<EdmType>(DataSpace.CSpace).Where(p => p.NamespaceName != "Edm" && p.Name == propertyDescriptor.ComponentType.Name))
10             {
11                 ReadOnlyMetadataCollection<Facet> facetList = (edmType as EntityType).Properties.First(p => p.Name == propertyDescriptor.Name).TypeUsage.Facets;
12                 facetList.Any(p =>
13                 {
14                     if (p.Name == "MaxLength")
15                     {
16                         if ((int)p.Value < value.ToString().Length)
17                             bindingContext.ModelState.AddModelError(propertyDescriptor.Name, "Company must be at most " + p.Value + " characters long");
18                         return true;
19                     }
20                     return false;
21                 });
22             }
23         }
24 
25     }

 

这样来耦合问题解决,大大减少了工作量。上面的代码需要优化,每个对象的属性绑定时都会调用,剩下的问题自己解决吧。将这个类注册下就可以了。

 

1         protected void Application_Start()
2         {
3             AreaRegistration.RegisterAllAreas();
4 
5             ModelBinders.Binders.Add(typeof(DataFacotry.Entity.Customer), new GoodsMvc.Component.ValidationModelBinder());
6 
7             RegisterRoutes(RouteTable.Routes);
8         }

同时也可以通过重载OnModelUpdated, 具体区别我也没有测试。

 

1         protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)

 

 这样看其来万事大吉了。 但我还是倾向于给ADO.Net Entity 生成的Model 添加CustomerAttribute. 到底能不能工作量少的情况下给所有的Filed 添加CustomerAttribute 呢?

  • 今天的重头戏来了,就是修改ADO.Net Entity 的生成GeneratorTemplate (T4 template)的实现。语法可以参考:http://msdn.microsoft.com/en-us/library/bb126478.aspx 在数据模型(.edmx file)的试图模式下,右键->Add code generation Item... 

可以搜索DataMemberAttribute在模板文件.tt中,定位到以下代码,添加高亮代码。

 1     /// <summary>
 2     /// <#=SummaryComment(primitiveProperty)#>
 3     /// </summary><#=LongDescriptionCommentElement(primitiveProperty, 1)#>
 4     [EdmScalarPropertyAttribute(EntityKeyProperty=<#=code.CreateLiteral(ef.IsKey(primitiveProperty))#>, IsNullable=<#=code.CreateLiteral(ef.IsNullable(primitiveProperty))#>)]
 5     [DataMemberAttribute()]    
 6 <#+ if(primitiveProperty.TypeUsage.Facets.Any(p=>p.Name=="MaxLength")){#>
 7     [ValidateLength(<#= primitiveProperty.TypeUsage.Facets["MaxLength"].Value #>)] 
 8 <#+
 9 }
10 #>

ValidataLength 继承于ValidationAttribute。

 

aaaaaaaaaa