我们在写程序当中, 经常需要检验数据是否符合规则, 比如不能为空, 必需为Email或手机格式等.
于是, 在我们的代码中, 就会出现很多重复的诸如此类的校验代码.
然后我就想到, 用Attribute的方式, 为需要校验的对象属性加上规则, 再用统一的方法去获取并校验数据是否符合规则.
Attribute的定义如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
![](/Images/OutliningIndicators/None.gif)
namespace FastDev.Core
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 自定义业务实体校验规则Attribute, 标记在属性
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class CheckAttribute : Attribute
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public CheckAttribute()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](/Images/OutliningIndicators/InBlock.gif)
}
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 检验的对象名称
/// </summary>
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string TargetName
{ get; set; }
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 校验的类型
/// </summary>
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public CheckStrategyType CheckStrategy
{ get; set; }
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 校验的参数, 仅在使用正则校验时有效
/// </summary>
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public object StrategyParam
{ get; set; }
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 校验失败时的错误信息, 仅在使用正则校验时有效
/// </summary>
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string CustomError
{ get; set; }
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 要校验的数据类型
/// </summary>
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public ValidateType ValidateType
{ get; set; }
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 允许校验的条件
/// </summary>
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public CheckWhen When
{ get; set; }
}
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 自定义业务实体校验规则Attribute, 标记在类声明上
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)]
public class ObjectCheckAttribute : CheckAttribute
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string PropertyName
{ get; set; }
}
}
![](/Images/OutliningIndicators/None.gif)
说明:
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;
![](/Images/OutliningIndicators/None.gif)
namespace FastDev.Core.Test
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
//ObjectCheck值不能为Null
[ObjectCheck(PropertyName = "ObjectCheck", CheckStrategy = CheckStrategyType.NotNull)]
public class EntityInfo
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//CheckNull值在Update和Delete时不能为Null
[Check(CheckStrategy = CheckStrategyType.NotNull, When = CheckWhen.OnUpdate | CheckWhen.OnDelete)]
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public object CheckNull
{ get; set; }
![](/Images/OutliningIndicators/InBlock.gif)
//CheckMail值必需是Email格式的
[Check(CheckStrategy = CheckStrategyType.Regex, ValidateType = ValidateType.Mail)]
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string CheckMail
{ get; set; }
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public object ObjectCheck
{ get; set; }
}
![](/Images/OutliningIndicators/InBlock.gif)
public class Entity
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//StringLength值必需符合^[a-zA-Z0-9]{4,10}$正则规则.
[Check(CheckStrategy = CheckStrategyType.RegexCustom, StrategyParam = @"^[a-zA-Z0-9]{4,10}$")]
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string StringLength
{ get; set; }
}
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// Summary description for CheckerTest
/// </summary>
[TestClass]
public class CheckerTest
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
[TestMethod]
public void TestCheckObject()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
EntityInfo entity = new EntityInfo()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
CheckNull = null, CheckMail = null, ObjectCheck = new object()
};
bool success = Checker.Default.CheckObject(entity);
Assert.AreEqual(false, success);
![](/Images/OutliningIndicators/InBlock.gif)
entity.CheckNull = new object();
Assert.AreEqual(false, Checker.Default.CheckObject(entity));
![](/Images/OutliningIndicators/InBlock.gif)
entity.CheckMail = "xiaosonl@gmail.com";
Assert.AreEqual(true, Checker.Default.CheckObject(entity));
![](/Images/OutliningIndicators/InBlock.gif)
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));
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
entity.CheckNull = new object();
Assert.AreEqual(true, Checker.Default.CheckObject(entity));
entity.ObjectCheck = null;
Assert.AreEqual(false, Checker.Default.CheckObject(entity));
}
![](/Images/OutliningIndicators/InBlock.gif)
[TestMethod]
public void TestCheckWhen()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
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);
}
![](/Images/OutliningIndicators/InBlock.gif)
[TestMethod]
public void TestCheckByRegexCustom()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Entity entity = new Entity();
entity.StringLength = "abc123";
Assert.AreEqual(true, Checker.Default.CheckObject(entity));
entity.StringLength = "^-";
Assert.AreEqual(false, Checker.Default.CheckObject(entity));
}
}
}
![](/Images/OutliningIndicators/None.gif)
好了,今天先介绍到这.其它的使用之后再继续介绍.
这是FastDev.Core程序集中的功能.规则定义不支持继承和Interface, 下个版本打算实现这个功能.
说到这里, 顺带说一下, 将Model层使用接口表示的好处.一是可以在ORM的Entity和普通ADO.NET的Entity中相互替换.前期可以用ORM快速开发,后期可以换ADO.NET提升性能.跟把数据访问层用接口表示来实现底层数据库类型替换是一个道理.二是可以只在一个地方定义业务数据规则,多种Model都可以适用, 这也是我开发这个的一大目的.
源代码下载: FastDev.Core.rar