代码改变世界

FastDev介绍系列之[数据规则验证](上)

2008-05-04 00:04  xiaosonl  阅读(2312)  评论(3编辑  收藏  举报

我们在写程序当中, 经常需要检验数据是否符合规则, 比如不能为空, 必需为Email或手机格式等.
于是, 在我们的代码中, 就会出现很多重复的诸如此类的校验代码.

然后我就想到, 用Attribute的方式, 为需要校验的对象属性加上规则, 再用统一的方法去获取并校验数据是否符合规则.

Attribute的定义如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FastDev.Core
{
    
/// <summary>
    
/// 自定义业务实体校验规则Attribute, 标记在属性
    
/// </summary>

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
    
public class CheckAttribute : Attribute
    
{
        
public CheckAttribute()
        
{

        }


        
/// <summary>
        
/// 检验的对象名称
        
/// </summary>

        public string TargetName getset; }

        
/// <summary>
        
/// 校验的类型
        
/// </summary>

        public CheckStrategyType CheckStrategy getset; }

        
/// <summary>
        
/// 校验的参数, 仅在使用正则校验时有效
        
/// </summary>

        public object StrategyParam getset; }

        
/// <summary>
        
/// 校验失败时的错误信息, 仅在使用正则校验时有效
        
/// </summary>

        public string CustomError getset; }

        
/// <summary>
        
/// 要校验的数据类型
        
/// </summary>

        public ValidateType ValidateType getset; }

        
/// <summary>
        
/// 允许校验的条件
        
/// </summary>

        public CheckWhen When getset; }
    }


    
/// <summary>
    
/// 自定义业务实体校验规则Attribute, 标记在类声明上
    
/// </summary>

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)]
    
public class ObjectCheckAttribute : CheckAttribute
    
{
        
public string PropertyName getset; }
    }

}


说明:
1.TargetName, 也就是待校验值的描述名称.如果只是一般的返回True或False(之后会继续提到其它的校验方式), 那么这个值可以不用.我们今天就不用.
2.CheckStrategy, 最重要的属性, Enum类型, 表示数据规则.暂时支持的只有:
   1)CheckStrategyType.NotNull.  不为Null.
   2)CheckStrategyType.NotNullOrEmpty.  不为Null,且如果为数组类型的话,数组长度也不以为0.
   3)CheckStrategyType.Regex.  正则规则.当使用这个时, 需指定ValidateType的值, Enum类型, 如ValidateType.Mail等, 表示要符合Email格式.
   4)CheckStrategyType.RegexCustom.  自定义正则规则.当CheckStrategyType.Regex无法满足需要时, 可使用此项. 自定义规则在StrategyParam属性中定义.
 3.PropertyName.  当Attribute不能定义在属性上时, 也可以定义上类上.只是要指定.PropertyName, 即要校验属性的名称.
 4.
When.  很常见的一种情况是, 比如说, 当我们要修改一条记录时,要求其ID不能为Null, 但是它在Insert的时候却又是可以为Null的, 因为值是Insert后生成的.这时候, 就可以设置When = CheckWhenWhen.Update.那删除的时候也要校验怎么办.一样的, When = CheckWhen.OnUpdate | CheckWhen.OnDelete.
用"|"组合就可以了, 这是用到二进制Enum的运算.

说了这么多, 让我们来看看几个使用示例.

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using FastDev.Core.Exception;
using System.Text.RegularExpressions;

namespace FastDev.Core.Test
{
    
//ObjectCheck值不能为Null
    [ObjectCheck(PropertyName = "ObjectCheck", CheckStrategy = CheckStrategyType.NotNull)]
    
public class EntityInfo
    
{
        
//CheckNull值在Update和Delete时不能为Null
        [Check(CheckStrategy = CheckStrategyType.NotNull, When = CheckWhen.OnUpdate | CheckWhen.OnDelete)]
        
public object CheckNull getset; }

        
//CheckMail值必需是Email格式的
        [Check(CheckStrategy = CheckStrategyType.Regex, ValidateType = ValidateType.Mail)]
        
public string CheckMail getset; }

        
public object ObjectCheck getset; }
    }


    
public class Entity
    
{
        
//StringLength值必需符合^[a-zA-Z0-9]{4,10}$正则规则.
        [Check(CheckStrategy = CheckStrategyType.RegexCustom, StrategyParam = @"^[a-zA-Z0-9]{4,10}$")]
        
public string StringLength getset; }
    }


    
/// <summary>
    
/// Summary description for CheckerTest
    
/// </summary>

    [TestClass]
    
public class CheckerTest
    
{
        [TestMethod]
        
public void TestCheckObject()
        
{
            EntityInfo entity 
= new EntityInfo()
            
{
                CheckNull 
= null, CheckMail = null, ObjectCheck = new object()
            }
;
            
bool success = Checker.Default.CheckObject(entity);
            Assert.AreEqual(
false, success);

            entity.CheckNull 
= new object();
            Assert.AreEqual(
false, Checker.Default.CheckObject(entity));

            entity.CheckMail 
= "xiaosonl@gmail.com";
            Assert.AreEqual(
true, Checker.Default.CheckObject(entity));

            entity.CheckNull 
= null;
            Assert.AreEqual(
true, Checker.Default.CheckObject(entity, CheckWhen.OnInsert));
            Assert.AreEqual(
false, Checker.Default.CheckObject(entity, CheckWhen.OnUpdate));
            Assert.AreEqual(
false, Checker.Default.CheckObject(entity, CheckWhen.OnDelete));


            entity.CheckNull 
= new object();
            Assert.AreEqual(
true, Checker.Default.CheckObject(entity));
            entity.ObjectCheck 
= null;
            Assert.AreEqual(
false, Checker.Default.CheckObject(entity));
        }


        [TestMethod]
        
public void TestCheckWhen()
        
{
            CheckWhen attWhen 
= CheckWhen.OnUpdate | CheckWhen.OnDelete;
            Assert.AreEqual(
true, (CheckWhen.OnUpdate | attWhen) == attWhen);
            Assert.AreEqual(
true, (CheckWhen.OnDelete | attWhen) == attWhen);
            Assert.AreEqual(
false, (CheckWhen.OnInsert | attWhen) == attWhen);
            Assert.AreEqual(
true, (CheckWhen.Any | attWhen) == attWhen);
        }


        [TestMethod]
        
public void TestCheckByRegexCustom()
        
{
            Entity entity 
= new Entity();
            entity.StringLength 
= "abc123";
            Assert.AreEqual(
true, Checker.Default.CheckObject(entity));
            entity.StringLength 
= "^-";
            Assert.AreEqual(
false, Checker.Default.CheckObject(entity));
        }

    }

}


好了,今天先介绍到这.其它的使用之后再继续介绍.
这是FastDev.Core程序集中的功能.规则定义不支持继承和Interface, 下个版本打算实现这个功能.
说到这里, 顺带说一下, 将Model层使用接口表示的好处.一是可以在ORM的Entity和普通ADO.NET的Entity中相互替换.前期可以用ORM快速开发,后期可以换ADO.NET提升性能.跟把数据访问层用接口表示来实现底层数据库类型替换是一个道理.二是可以只在一个地方定义业务数据规则,多种Model都可以适用, 这也是我开发这个的一大目的.
源代码下载: FastDev.Core.rar