详解ASP.NET MVC中属性标记的通用扩展方法(2)
xVal可以通过IRulesProvider接口,通过这个接口可以进行扩展,很明显,它只扩展了Castle框架跟NHibernate框架,通过如下两个程序集就可以看出来:
xVal.RulesProviders.CastleValidator.dllxVal.RulesProviders.NHibernateValidator.dll基本上可以得出结论:xVal没有提供对Entityframework框架的扩展,还需要我们做扩展。
最终,网上的一片文章给了我提示,问题得到了解决,解决的思路就是建立一个伙伴类,这个伙伴类跟原来的类的结构定义是一样的,在进行验证的时候,不对edm文件中的类进行验证,而是对伙伴类进行验证。
这里就以xVal框架为例进行Demo演示吧。
首先我们建立一个类模拟Entityframework生成的edm文件中的类,类的定义代码如下:
模拟EF中的User类
- public partial class User
- {
- public string UserName { get; set; }
- public string Password { get; set; }
- public string Address { get; set; }
- public string Telephone { get; set; }
- public int Age { get; set; }
- public string Email { get;set;}
- }
接下来我们建立一个伙伴类
伙伴类的代码
- public class UserMetadata
- {
- [Required]
- [StringLength(10)]
- public string UserName { get; set; }
- [Required]
- [StringLength(18)]
- [DataType(DataType.Password)]
- public string Password { get; set; }
- [Required]
- [StringLength(100)]
- public string Address { get; set; }
- [Required]
- [DataType(DataType.PhoneNumber)]
- public string Telephone { get; set; }
- [Required]
- [Range(1, 100)]
- public int Age { get; set; }
- [Required]
- [DataType(DataType.EmailAddress)]
- public string Email { get; set; }
- }
再接下来,我们使用partial关键字为User类进行扩展,扩展类的定义如下:
扩展类的定义
- [MetadataType(typeof(UserMetadata))]
- public partial class User
- {
- }
注意这段代码:[MetadataType(typeof(UserMetadata))]
为了方便大家阅读,我把整体代码贴出来,整体代码如下:
整体代码
- using System.ComponentModel.DataAnnotations;
- namespace MVCValidate.Models
- {
- public partial class User
- {
- public string UserName { get; set; }
- public string Password { get; set; }
- public string Address { get; set; }
- public string Telephone { get; set; }
- public int Age { get; set; }
- public string Email { get;set;}
- }
- [MetadataType(typeof(UserMetadata))]
- public partial class User
- {
- }
- public class UserMetadata
- {
- [Required]
- [StringLength(10)]
- public string UserName { get; set; }
- [Required]
- [StringLength(18)]
- [DataType(DataType.Password)]
- public string Password { get; set; }
- [Required]
- [StringLength(100)]
- public string Address { get; set; }
- [Required]
- [DataType(DataType.PhoneNumber)]
- public string Telephone { get; set; }
- [Required]
- [Range(1, 100)]
- public int Age { get; set; }
- [Required]
- [DataType(DataType.EmailAddress)]
- public string Email { get; set; }
- }
- }
接下来,我们要实现伙伴类跟原类的替换方法了,代码如下所示:
DataAnnotationsValidationRunner类的代码
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.ComponentModel.DataAnnotations;
- using System.Linq;
- using xVal.ServerSide;
- namespace MVCValidate.Models
- {
- internal static class DataAnnotationsValidationRunner
- {
- // TODO: DOES NOT SUPPORT METADATA TYPE
- ///// Warning: For some reason, DataTypeAttribute.IsValid() always returns "true", regardless of whether
- ///// it is actually valid. Need to improve this test runner to fix that.
- //public static IEnumerable<ErrorInfo> GetErrors(object instance)
- //{
- // return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
- // from attribute in prop.Attributes.OfType<ValidationAttribute>()
- // where !attribute.IsValid(prop.GetValue(instance))
- // select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
- //}
- /// <summary>
- /// Get any errors associated with the model also investigating any rules dictated by attached Metadata buddy classes.
- /// </summary>
- /// <param name="instance"></param>
- /// <returns></returns>
- public static IEnumerable<ErrorInfo> GetErrors(object instance)
- {
- var metadataAttrib = instance.GetType().GetCustomAttributes(typeof(MetadataTypeAttribute), true)
- .OfType<MetadataTypeAttribute>().FirstOrDefault();
- var buddyClassOrModelClass = metadataAttrib != null ? metadataAttrib.MetadataClassType : instance.GetType();
- var buddyClassProperties = TypeDescriptor.GetProperties(buddyClassOrModelClass).Cast<PropertyDescriptor>();
- var modelClassProperties = TypeDescriptor.GetProperties(instance.GetType()).Cast<PropertyDescriptor>();
- return from buddyProp in buddyClassProperties
- join modelProp in modelClassProperties on buddyProp.Name equals modelProp.Name
- from attribute in buddyProp.Attributes.OfType<ValidationAttribute>()
- where !attribute.IsValid(modelProp.GetValue(instance))
- select new ErrorInfo(buddyProp.Name, attribute.FormatErrorMessage(string.Empty), instance);
- }
- }
- }
完成以上的代码以后,大部分工作就完成了,接下来,我们在Controller中编写一个create方法,来模拟Create操作,代码如下所示:
Controller层的代码
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using System.Web.Mvc.Ajax;
- using MVCValidate.Models;
- using xVal.ServerSide;
- namespace MVCValidate.Controllers
- {
- public class UserController : Controller
- {
- [AcceptVerbs(HttpVerbs.Post)]
- public ActionResult Create(User user)
- {
- var errors = DataAnnotationsValidationRunner.GetErrors(user);
- if (errors.Any())
- {
- new RulesException(errors).AddModelStateErrors(ModelState,"user");
- }
- return View();
- }
- }
- }
接下来,编写View层的代码,比较简单,我就直接贴出来了,代码如下:
View层的代码
- <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MVCValidate.Models.User>" %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" >
- <head runat="server">
- <title>Create</title>
- </head>
- <body>
- <%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
- <% using (Html.BeginForm()) {%>
- <fieldset>
- <legend>Fields</legend>
- <p>
- <label for="UserName">UserName:</label>
- <%= Html.TextBox("user.UserName") %>
- <%= Html.ValidationMessage("user.UserName")%>
- </p>
- <p>
- <label for="Password">Password:</label>
- <%= Html.TextBox("user.Password") %>
- <%= Html.ValidationMessage("user.Password")%>
- </p>
- <p>
- <label for="Address">Address:</label>
- <%= Html.TextBox("user.Address")%>
- <%= Html.ValidationMessage("user.Address")%>
- </p>
- <p>
- <label for="Telephone">Telephone:</label>
- <%= Html.TextBox("user.Telephone")%>
- <%= Html.ValidationMessage("user.Telephone")%>
- </p>
- <p>
- <label for="Age">Age:</label>
- <%= Html.TextBox("user.Age")%>
- <%= Html.ValidationMessage("user.Age")%>
- </p>
- <p>
- <label for="Email">Email:</label>
- <%= Html.TextBox("user.Email")%>
- <%= Html.ValidationMessage("user.Email")%>
- </p>
- <p>
- <input type="submit" value="Create" />
- </p>
- </fieldset>
- <% } %>
- <div>
- <%=Html.ActionLink("Back to List", "Index") %>
- </div>
- </body>
- </html>
最终的效果如下图所示:

ASP.NET MVC开源验证框架非常的多,只是有相似问题的更多,有了这个通用的方法,就可以很容易对其他验证框架进行扩展了。
- 最新评论 查看所有评论
-
- 发表评论 查看所有评论
-
- GRIDVIEW排序 动态实现和静态实现
- ASP.NET2.0数据操作之创建数据访问层
- 单点登录的实现
- ASP.NET程序中常用代码汇总
- ASP.NET中实时图表的实现
- ASP.NET中利用Crystal Report创建图表
- ASP.NET 2.0中Gridview控件高级技巧
- VB中String的用法及原理
- ASP.NET 2.0轻松实现数据库应用开发
- ASP.NET AJAX入门系列(1):概述
- HijackThis使用详解
- 网友整理 .net 面试题(附答案)
- Visual Studio 2005中编译调试
- ASP.NET AJAX入门系列(2):使用ScriptManager控
- ASP.NET2.0中Gridview中数据操作技巧
