一起谈.NET技术,ASP.NET MVC验证框架中关于属性标记的通用扩展方法

  之前写过一篇文章《ASP.NET MVC中的验证》,唯一的遗憾就是在使用Data Annotation Validators方式验证的时候,如果数据库是Entityframework等自动生成的文件,就没有办法使用扩展属性标记进行标记。现在已经开始有了一些其它的Asp.net MVC 验证框架,使用上跟Data Annotation Validators差不太多,但是普遍有这样的问题,如果数据库是Entityframework生成的edm文件,没有办法进行扩展属性标记。

  今天在网上发现了另外一个 Asp.net MVC 验证框架---xVal框架,使用上跟Data Annotation Validators非常接近,也有类似的问题。

  简单介绍下,xVal是一个开源的asp.net mvc验证框架,有关它的介绍,可以参考:《xVal - a validation framework for ASP.NET MVC

  xVal使用了MS-PL的开源协议 ,也就是说,它允许用户看、修改和分发源代码,而不论出自商业用途还是非商业用途,类似BSD许可证。

 

  xVal可以通过IRulesProvider接口,通过这个接口可以进行扩展,很明显,它只扩展了Castle框架跟NHibernate框架,通过如下两个程序集就可以看出来:


xVal.RulesProviders.CastleValidator.dll
xVal.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开源验证框架非常的多,只是有相似问题的更多,有了这个通用的方法,就可以很容易对其他验证框架进行扩展了。

   最后,为了方便大家学习,代码我进行了打包,下载地址在这里:

 代码下载

  【参考文章】:

 《Using MetadataType attribute with ASP.NET MVC xVal Validation Framework

时间: 2024-10-02 00:21:48

一起谈.NET技术,ASP.NET MVC验证框架中关于属性标记的通用扩展方法的相关文章

《ASP.NET MVC验证框架中关于属性标记的通用扩展方法》之继续扩展

首先需要对xVal有点熟悉: http://www.codeplex.com/xval 建议下载最新源码而不是编译版本 再看两篇文章: http://goneale.com/2009/03/04/using-metadatatype-attribute-with-aspnet-mvc-xval- validation-framework/ 深山老林将之翻译为:<ASP.NET MVC验证框架中关于属性标记的通用扩展方法> http://www.cnblogs.com/wlb/archive/2

ASP.NET MVC验证框架中关于属性标记的通用扩展方法

之前写过一篇文章<ASP.NET MVC中的验证>,唯一的遗憾就是在使用Data Annotation Validators方式验证的时候,如果数据库是Entityframework等自动生成的文件,就没有办法使用扩展属性标记进行标记.现在已经开始有了一些其它的Asp.net MVC 验证框架,使用上跟Data Annotation Validators差不太多,但是普遍有这样的问题,如果数据库是Entityframework生成的edm文件,没有办法进行扩展属性标记. 今天在网上发现了另外一

解决Asp.net Mvc返回JsonResult中DateTime类型数据格式问题的方法_实用技巧

问题背景:            在使用asp.net mvc 结合jquery esayui做一个系统,但是在使用使用this.json方法直接返回一个json对象,在列表中显示时发现datetime类型的数据在转为字符串是它默认转为Date(84923838332223)的格式,在经过查资料发现使用前端来解决这个问题的方法不少,但是我又发现在使用jquery easyui时,加载列表数据又不能对数据进行拦截,进行数据格式转换之后再加载,后来发现可以通过自定义JsonResult实现,认为这种

一起谈.NET技术,ASP.NET MVC 验证方式(1)

实现一个简单的验证(C#) 本部分主要目的是说明如何在ASP.NET MVC应用程序中实现验证.例如,将了解如何验证一个表单提交时必填内容的合法性.了解如何应用model state和验证相关的HTML helpers. 理解Model State 你使用model state,更准确点说是使用model state 字典去表示验证错误.比如,列表一中的Create()方法,在产品类保存入库之前去验证产品类的属性. 把验证和数据逻辑加到控制类中是违反编程方法的,控制类中应该仅包含程序的流程逻辑.

ASP.NET MVC+EF框架+EasyUI实现权限管理系列(17)-注册用户功能的细节处理(各种验证)

原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(17)-注册用户功能的细节处理(各种验证) ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇)   (1):框架搭建    (2):数据库访问层的设计Demo    (3):面向接口编程   (4 ):业务逻辑层的封装    (5):前台Jquery easyUI实现    (6):EF上下文实例管理    (7):DBSession的封装   (8):DBSession线程内唯一     (9):TT摸版

Asp.net MVC验证那些事(1)-- 介绍和验证规则使用

原文  Asp.net MVC验证那些事(1)-- 介绍和验证规则使用 数据的有效性验证,是程序开发中必不可少的环节.这篇文章,我们将用一个实例来说明如何在MVC中使用Validation来保证无论是新增或者更新数据的时候,都必须符合我们定义的验证规则,否则就会提示错误. 一, DRY原则 DRY是Don't repeat yourself的缩写,意思是不要写重复的代码.MVC中的Validation设计就是遵循了这个原则. 比如下面中的Movie类, 当用户在网页上填写Movie信息的时候,并

Asp.net MVC验证哪些事(3)-- Remote验证及其改进(附源码)

原文 Asp.net MVC验证哪些事(3)-- Remote验证及其改进(附源码) 表单中的输入项,有些是固定的,不变的验证规则,比如字符长度,必填等.但有些是动态的,比如注册用户名是否存在这样的检查,这个需要访问服务器后台才能解决.这篇文章将会介绍MVC中如何使用[RemoteAttribute]来解决这类验证需求,同时会分析[RemoteAttribute]的不足,以及改进的方法. 本文相关的源代码在这里 MVC-Remote-Validation.zip 一, RemoteAttribu

ASP.NET MVC 验证方式(1)

实现一个简单的验证(C#) 本部分主要目的是说明如何在ASP.NET MVC应用程序中实现验证.例如,将了解如何验证一个表单提交时必填内容的合法性.了解如何应用model state和验证相关的HTML helpers. 理解Model State 你使用model state,更准确点说是使用model state 字典去表示验证错误.比如,列表一中的Create()方法,在产品类保存入库之前去验证产品类的属性. 把验证和数据逻辑加到控制类中是违反编程方法的,控制类中应该仅包含程序的流程逻辑.

Asp.net MVC验证哪些事(2)-- 验证规则总结以及使用

原文 Asp.net MVC验证哪些事(2)-- 验证规则总结以及使用 上篇文章Asp.net MVC验证那些事(1)-- 介绍和验证规则使用中,介绍了Asp.net MVC中的验证功能以及如何使用.这里将对MVC中内置的验证规则进行总结. 一,查找所有验证规则 上篇文章中提到,所有的验证规则都是位于下面的命名空间下: using System.ComponentModel.DataAnnotations; 我们可以用ILSpy反编译工具,看看这个命名空间下都有些什么. 下面的图让我们一目了然,