爱上MVC3~实体级标准验证
在MVC架构中,底层数据源一般为linq to sql或者entity frameworks,可能还有少数团队使用自己的ORM工具,但对于实体级的数据验证来说都是必须要干的事,你可能在viewmodel中重复的写一些验证规则,你可能在用ctrl+C和ctrl+V的同时,也知道自己违背了DRY原则,但你可能会说:“这样干容易,简单!“,”我的逻辑复杂,我要分情况去考虑幼!”,呵呵,这些对于我来说,都是借口,违背就是违背,可能有些时候需要去违背原则,就像SQL非范式一样,但今天要说的是,如何为一个实体对象,设计一个完整统一的验证规则,我之前写过一些实体验证的东西,而今天说的,主要还是使用“attribute"来实现这个验证!
对于linq to sql,ef的开发者来说,实体对象会为我们自己生成,而我们对实体的验证不可能写在生成的实体中,因为只要实体要去从数据库更新,你所作的修改就徒劳了,呵呵 ,还要小微早就为我们考虑到了,所以,所有的实体类都是partial class,我们喜欢叫它“分部类”。
一些的效果为:
而一般我们的作法是把验证信息写在ViewModel里,即一个View写一个Model,再添加一些验证信息,而这认为这需要具体问题具体分析了,我们不应该将所以业务都去重新抽象,这样无疑加大了代码量,也破坏了实体完整性,一个东西为何拆成多个?
事实上,数据验证这种事,我们往往应该把它放在数据模型层去干这事,即Entity(Model)层,它体现了数据库的抽象,包括DATA层在内的所有层都可以去引用它,它只是数据库的映射与数据有效性的验证,不存在数据的操作,而操作这种事我们留给了DATA层,而根据业务去组合操作这种事我们交给了BLL(Service)层!
OK,我们来看一下,我在Entity层对实体的设计:
1 namespace Role.Entity 2 { 3 /// <summary> 4 /// WorkFlow_Info数据有效性验证 5 /// </summary> 6 public class WorkFlow_Info_Meta 7 { 8 public int ID { get; set; } 9 public Nullable<int> PrevNode { get; set; } 10 public Nullable<int> NextNode { get; set; } 11 [Required(ErrorMessage = "请填写工作流名称")] 12 [Display(Name = "工作流名称")] 13 public string Name { get; set; } 14 public string Info { get; set; } 15 } 16 /// <summary> 17 /// 工作流实体 18 /// </summary> 19 [MetadataType(typeof(WorkFlow_Info_Meta))] 20 public partial class WorkFlow_Info 21 { 22 #region 导航属性 23 /// <summary> 24 /// 是否为首结点 25 /// </summary> 26 public bool IsFirstNode 27 { 28 get 29 { 30 return this.PrevNode == null; 31 } 32 } 33 /// <summary> 34 /// 是否为尾结点 35 /// </summary> 36 public bool IsEndNode 37 { 38 get 39 { 40 return this.NextNode == null; 41 } 42 } 43 44 #endregion 45 } 46 /// <summary> 47 /// 工作量实体扩展 48 /// </summary> 49 public class WorkFlow_Info_Ext : WorkFlow_Info 50 { 51 } 52 }
好了,而对于前台提交表单时,只要调用ModelState对象的IsValid属性即可验证前台模型了,呵呵!
1 [HttpPost] 2 public ActionResult Edit(WorkFlow_Info entity) 3 { 4 if (ModelState.IsValid) 5 { 6 repository.Update(entity); 7 return RedirectToAction("Index"); 8 } 9 return View(); 10 }