詳解ASP.NET MVC中屬性標(biāo)記的通用擴(kuò)展方法
本文將討論的是ASP.NET MVC中屬性標(biāo)記的通用擴(kuò)展方法,在這里我們也將更進(jìn)一步的了解ASP.NET MVC的作用。希望大家能用好ASP.MVC。
#T#
之前寫過一篇文章《ASP.NET MVC中的驗(yàn)證》,唯一的遺憾就是在使用Data Annotation Validators方式驗(yàn)證的時候,如果數(shù)據(jù)庫是Entityframework等自動生成的文件,就沒有辦法使用擴(kuò)展屬性標(biāo)記進(jìn)行標(biāo)記?,F(xiàn)在已經(jīng)開始有了一些其它的ASP.NET MVC 驗(yàn)證框架,使用上跟Data Annotation Validators差不太多,但是普遍有這樣的問題,如果數(shù)據(jù)庫是Entityframework生成的edm文件,沒有辦法進(jìn)行擴(kuò)展屬性標(biāo)記。
今天在網(wǎng)上發(fā)現(xiàn)了另外一個 ASP.NET MVC 驗(yàn)證框架---xVal框架,使用上跟Data Annotation Validators非常接近,也有類似的問題。
簡單介紹下,xVal是一個開源的ASP.NET MVC驗(yàn)證框架,有關(guān)它的介紹,可以參考:《xVal - a validation framework for ASP.NET MVC》
xVal使用了MS-PL的開源協(xié)議 ,也就是說,它允許用戶看、修改和分發(fā)源代碼,而不論出自商業(yè)用途還是非商業(yè)用途,類似BSD許可證。
xVal可以通過IRulesProvider接口,通過這個接口可以進(jìn)行擴(kuò)展,很明顯,它只擴(kuò)展了Castle框架跟NHibernate框架,通過如下兩個程序集就可以看出來:
xVal.RulesProviders.CastleValidator.dllxVal.RulesProviders.NHibernateValidator.dll基本上可以得出結(jié)論:xVal沒有提供對Entityframework框架的擴(kuò)展,還需要我們做擴(kuò)展。
最終,網(wǎng)上的一片文章給了我提示,問題得到了解決,解決的思路就是建立一個伙伴類,這個伙伴類跟原來的類的結(jié)構(gòu)定義是一樣的,在進(jìn)行驗(yàn)證的時候,不對edm文件中的類進(jìn)行驗(yàn)證,而是對伙伴類進(jìn)行驗(yàn)證。
這里就以xVal框架為例進(jìn)行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關(guān)鍵字為User類進(jìn)行擴(kuò)展,擴(kuò)展類的定義如下:
擴(kuò)展類的定義
- [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; }
- }
- }
接下來,我們要實(shí)現(xiàn)伙伴類跟原類的替換方法了,代碼如下所示:
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開源驗(yàn)證框架非常的多,只是有相似問題的更多,有了這個通用的方法,就可以很容易對其他驗(yàn)證框架進(jìn)行擴(kuò)展了。
原文標(biāo)題:ASP.NET MVC驗(yàn)證框架中關(guān)于屬性標(biāo)記的通用擴(kuò)展方法
鏈接:http://www.cnblogs.com/wlb/archive/2009/12/01/1614209.html