一、基础说明
1.开发这个组件的背景
线上系统经常遇到以下情况:
- 某个字段由必填切换成非必填
- DB的长度不能满足目前业务,比如由 30的长度 调整为 50
- 正则表达式之类的校验需要 扩充
当遇到类似上述情况时,基本都要重新修改代码,重新测试走发布流程,有时候还需要合并不同版本的代码(1.风险高 2.紧急情况时效无法保证)等;开发这个基于Database的校验组件(配置信息第一版本基于服务器内存进行缓存)是为了能够很方便的适应上述需求.
2.提示消息持的占位符
{FieldName} :字段名称占位符
{InputValue} :校验的输入值
{ValidValue} :有效值
3.校验组件使用的相关特性
Xxx.BI.Validate.ValidateFieldAttribute :核心校验特性
Xxx.Attributes.AliasAttribute : 别名特性
4.全局校验的默认格式
校验类型 | 格式化字符串 | 返回给用户的校验结果示例 |
---|---|---|
Required | {FieldName} is required. | 年龄 is required. |
MaxLength | {FieldName} maxlength is {ValidValue},the value {InputValue} invalid. | 手机号码 maxlength is 11,the value 086-15821573438 invalid. |
DecimalDigitsLength | {FieldName} decimal digits is {ValidValue},the value {InputValue} invalid. | D0 decimal digits is 0,the value 12.1 invalid. |
RangString | {FieldName} valid value must be contains in {ValidValue},the value {InputValue} invalid. | R1 valid value must be contains in |
RegexExpression | {FieldName} -> the value {InputValue} invalid. | 英文名 -> the value 苹果 invalid. |
4.已实现的功能
1.基于 Xxx.BI.Validate.ValidateFieldAttribute 配置的校验
2.基于 Database (支持MySQL、SQL Server) 配置的校验
以上 两种都配置,优先使用Database配置,非database会失效
二、仅配置 ValidateFieldAttribute 实现校验
1.仅ValidateFieldAttribute 特性配置说明
名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
Key | string | 校验唯一Key,不使用 对应 Sys_Validate_Field 表中的 Validate_Key 字段的值; | |
Remark | string | 备注,显示作用,无任何业务逻辑 | |
CSharpDataType | enum | String | 数据类型,在database中配置的时候不区分大小写;支持的枚举 -> [ Int,Long,String,Datetime,Decimal ] |
Required | bool | false | 是否必填;true:启用必填校验;false:禁用必填校验 |
MaxLength | int | -1 | 最大长度,小于等于0 则不生效此校验 |
DecimalDigitsLength | int | -1 | 小数位数长度校验,小于等于-1则不生效此校验 |
RegexExpression | string | 正则表达式消息 | |
RegexExpressionMsg | string | RegexExpression字段对应的提示消息 | |
RangeString | string | 范围; 1.字符串类型的范围校验配置用 | 隔开,开头和结尾不能省略,如: |男|女| . 2. 数字类型的范围,支持Int、Long、Decimal 类型,配置示例介绍 -> 1~99 ,表示大于等于1小于等于99;1~ 表示大于等于1;~ 99 表示小于等于 99 范围校验必须指定 CSharpDataType |
2.仅配置ValidateFieldAttribute使用示例
2.1 实体配置
using Xxx.Attributes;
namespace Xxx.BI.ValidateField.NUnit.DemoModel
{
/// <summary>
/// 没有数据库这种情况的校验配置
/// </summary>
/// <remarks>
/// <para/>Author : AnDequan
/// <para/>Date : 2021-05-19 14:58
/// </remarks>
public class NoDbValidateConfig
{
/// <summary>
/// 订单编号
/// </summary>
[ValidateField(CSharpDataType = EnumCSharpDataType.String, Required = true, MaxLength = 30)]
[Alias("订单编号")]
public string OrderNo { get; set; }
/// <summary>
/// Code
/// </summary>
[ValidateField(CSharpDataType = EnumCSharpDataType.String, Required = true, MaxLength = 30)]
[Alias("系统唯一代码")]
public string Code { get; set; }
/// <summary>
/// 中文名称
/// </summary>
[ValidateField(CSharpDataType = EnumCSharpDataType.String, MaxLength = 50)]
public string NameCn { get; set; }
/// <summary>
/// 英文名称,只能输入英文字母
/// </summary>
[ValidateField(CSharpDataType = EnumCSharpDataType.String, RegexExpression = "^[A-Za-z]+$", RegexExpressionMsg = "{FieldName} invalid.")]
public string NameEn { get; set; }
/// <summary>
/// 单价
/// </summary>
[ValidateField(CSharpDataType = EnumCSharpDataType.Decimal, MaxLength = 19, DecimalDigitsLength = 5)]
public decimal? Price { get; set; }
}
}
2.2 调用获取校验结果示例
using NUnit.Framework;
using System.Collections.Generic;
using Xxx.AuxiliaryEntity;
using Xxx.BI.ValidateField;
using Xxx.BI.ValidateField.NUnit.DemoModel;
namespace Xxx.BI.Validate.NUnit
{
/// <summary>
/// 验证Demo
/// </summary>
/// <remarks>
/// <para/>Author : AnDequan
/// <para/>Date : 2021-05-19 15:46
/// </remarks>
public class ValidateDemoNUnitTest
{
/// <summary>
/// Setup
/// </summary>
/// <remarks>
/// <para/>Author : AnDequan
/// <para/>Date : 2021-05-18 11:23
/// </remarks>
[SetUp]
public void Setup()
{
}
/// <summary>
/// 仅配置ValidateFieldAttribute使用示例 - Test
/// </summary>
/// <remarks>
/// <para/>Author : AnDequan
/// <para/>Date : 2021-05-18 11:23
/// </remarks>
[Test]
public void JustUseValidateFieldAttribute()
{
var orderNo = "ODR2021051900001";
NoDbValidateConfig model = new NoDbValidateConfig()
{
OrderNo = orderNo,
Code = "012345678901234567890123456789+1",
NameCn = "01234567890123456789012345678901234567890123456789+1",
NameEn = "01234567890123456789012345678901234567890123456789+1",
Price = 15.215515M
};
IValidateKit validate = new ValidateKit();
OperationResult result = validate.GetValidateResult(model, nameof(NoDbValidateConfig.OrderNo), true);
Dictionary<object, IList<string>> errorMsg = result.Data as Dictionary<object, IList<string>>;
Assert.IsTrue(
(!result.Success)
&& errorMsg.ContainsKey(orderNo)
&& errorMsg[orderNo][0] == "系统唯一代码 maxlength is 30,the value 012345678901234567890123456789+1 invalid."
&& errorMsg[orderNo][1] == "NameCn maxlength is 50,the value 01234567890123456789012345678901234567890123456789+1 invalid."
&& errorMsg[orderNo][2] == "NameEn invalid."
&& errorMsg[orderNo][3] == "Price decimal digits is 5,the value 15.215515 invalid."
);
}
/// <summary>
/// 仅配置ValidateFieldAttribute使用示例 - Test
/// </summary>
/// <remarks>
/// <para/>Author : AnDequan
/// <para/>Date : 2021-05-18 11:23
/// </remarks>
[Test]
public void JustUseValidateFieldAttributeList()
{
IList<NoDbValidateConfig> modelList = new NoDbValidateConfig[]
{
new NoDbValidateConfig()
{
OrderNo = "ODR2021051900002",
Code = "012345678901234567890123456789+1",
NameCn = "01234567890123456789012345678901234567890123456789+1",
NameEn = "01234567890123456789012345678901234567890123456789+1",
Price = 15.215515M
},
new NoDbValidateConfig()
{
OrderNo = "ODR2021051900003",
Code = "012345678901234567890123456789+1",
NameCn = "01234567890123456789012345678901234567890123456789+1",
NameEn = "01234567890123456789012345678901234567890123456789+1",
Price = 15.215515M
}
};
IValidateKit validate = new ValidateKit();
OperationResult result = validate.GetValidateResult(modelList, nameof(NoDbValidateConfig.OrderNo), true);
Dictionary<object, IList<string>> errorMsg = result.Data as Dictionary<object, IList<string>>;
Assert.IsTrue((!result.Success) && errorMsg.Count == 2);
}
}
}
三、配置 Database 实现校验
1. 必要表 Sys_Validate_Field 说明
名称 | 类型 | 必填 | 长度 | 默认值 | 说明 |
---|---|---|---|---|---|
Class_Name | string | Y | 255 | 类名称,用户自定义,程序中不使用 | |
Field_Name | string | Y | 255 | 字段名称,用户自定义,程序中不使用 | |
Validate_Key | string | Y | 255 | 唯一键,全库唯一建议:类名:字段名 | |
Validate_Type | string | Y | 255 | 验证的数据类型或方式,不区分大小写 | |
Regex_Expression | string | N | 2000 | 正则表达式 | |
Regex_Expression_Msg | string | N | 2000 | Regex Expression Message | |
Required | bool | Y | false | 是否必填;true:启用必填校验;false:禁用必填校验 | |
Required_Msg | string | N | 255 | 类名称,用户自定义,程序中不使用 | |
Max_Length | int | N | 最大长度,小于等于0 则不生效此校验 | ||
Max_Length_Msg | string | N | 2000 | 类名称,用户自定义,程序中不使用 | |
Decimal_Digits_Length | int | Y | 小数位数长度校验,小于等于-1则不生效此校验 | ||
Decimal_Digits_Length_Msg | string | N | 2000 | 类名称,用户自定义,程序中不使用 | |
Range_String | string | N | 2000 | 范围校验; 1.字符串类型的范围校验配置用 | 隔开,开头和结尾不能省略,如: |男|女| . 2. 数字类型的范围,支持Int、Long、Decimal 类型,配置示例介绍 -> 1~99 ,表示大于等于1小于等于99;1~ 表示大于等于1;~ 99 表示小于等于 99 范围校验必须指定 Validate_Type | |
Range_String_Msg | string | N | max | 类名称,用户自定义,程序中不使用 |
2.更改了 Sys_Validate_Field表中的配置,刷新缓存
调用: 域名+ /Validation/SysValidateField/RefreshCache
提示: 当改变数据库配置之后如需立即生效,则调用.
3.查看当前生效的 Sys_Validate_Field 表的配置
调用: 域名+/Validation/SysValidateField/GetCache
4.读取Sys_Validate_Field表配置示例
4.1 实体配置信息
using Xxx.Attributes;
namespace Xxx.BI.ValidateField.NUnit.Model
{
/// <summary>
/// 必填测试
/// </summary>
/// <remarks>
/// <para/>Author : AnDequan
/// <para/>Date : 2021-05-18 11:09
/// </remarks>
public class RequiredModel
{
/// <summary>
/// JobNo
/// </summary>
[ValidateField(Required = true)]
public string JobNo { get; set; }
/// <summary>
/// 年龄
/// </summary>
[ValidateField(Required = true)]
[Alias(Name = "年龄")]
public int? Age { get; set; }//[ 1.配置了 ValidateField 特性 2.配置了别名 ] An 2021-05-18 11:14
/// <summary>
/// Code
/// SQL备份:INSERT INTO Sys_Validate_Field ( Class_Name, Field_Name, Validate_Key, Validate_Type, Regex_Expression, Regex_Expression_Msg, Required, Required_Msg, Max_Length, Max_Length_Msg, Decimal_Digits_Length, Decimal_Digits_Length_Msg, Range_String, Range_String_Msg, CreateUserId, CreateUserName, CreateTime, Unique_No ) VALUES ( 'Sys_Parameter', 'Code', 'Sys_Parameter:Code', 'string', '', '', '1', '{FieldName} is 必填.', -1, '{FieldName} maxlength is {ValidValue}.', -1, '{FieldName} decimal digits is {ValidValue}.', '', '{FieldName} valid value must in ({ValidValue}).', 'System', 'System', 'May 18 2021 10:54AM', '4CA580EF-AC86-4299-AD3E-24A826370218' )
/// </summary>
[ValidateField(Key = "Sys_Parameter:Code")]
public string Code { get; set; }//[ 1.配置了 ValidateField 特性 走 DB的配置 ] An 2021-05-18 11:15
/// <summary>
/// 姓名
/// SQL备份:INSERT INTO Sys_Validate_Field ( Class_Name, Field_Name, Validate_Key, Validate_Type, Regex_Expression, Regex_Expression_Msg, Required, Required_Msg, Max_Length, Max_Length_Msg, Decimal_Digits_Length, Decimal_Digits_Length_Msg, Range_String, Range_String_Msg, CreateUserId, CreateUserName, CreateTime, Unique_No ) VALUES ( 'Sys_Parameter', 'Name_Cn', 'Sys_Parameter:Name_Cn', 'string', '', '', '0', '{FieldName} is 必填.', -1, '{FieldName} maxlength is {ValidValue}.', -1, '{FieldName} decimal digits is {ValidValue}.', '', '{FieldName} valid value must in ({ValidValue}).', 'System', 'System', 'May 18 2021 10:54AM', 'C1510DD7-ED27-409F-8E06-99DA81911B4D' )
/// 1.数据库中配置不必填 2.程序中配置必填;
/// 最终应该没有 必填提示
/// <para/>
/// </summary>
[ValidateField(Key = "Sys_Parameter:Name_Cn", Required = true)]
public string NameCn { get; set; }
}
}
4.2 调用结果示例如下
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using Xxx.AuxiliaryEntity;
using Xxx.BI.ValidateField.Contract;
using Xxx.BI.ValidateField.NUnit.Model;
using Xxx.BI.ValidateField.Service;
using Xxx.iW;
namespace Xxx.BI.ValidateField.NUnit
{
/// <summary>
/// 必填项测试
/// </summary>
/// <remarks>
/// <para/>Author : AnDequan
/// <para/>Date : 2021-05-18 10:30
/// </remarks>
public class ValidateNUnitTest
{
/// <summary>
/// 验证服务
/// </summary>
IValidationService ValidateService { get; set; }
/// <summary>
/// Setup
/// </summary>
/// <remarks>
/// <para/>Author : AnDequan
/// <para/>Date : 2021-05-18 11:23
/// </remarks>
[SetUp]
public void Setup()
{
IRepository _Repository; _Repository = new Repository<SqlConnection>("换成自己的连接字符串");
ValidateService = new ValidationService(_Repository);
}
/// <summary>
/// 必填校验
/// </summary>
/// <remarks>
/// <para/>Author : AnDequan
/// <para/>Date : 2021-05-18 11:23
/// </remarks>
[Test]
public void Required()
{
var jobNo = "R000001";
RequiredModel model = new RequiredModel() { JobNo = jobNo, };
IValidateKit validate = new ValidateKit(ValidateService);
OperationResult result = validate.GetValidateResult(model, nameof(RequiredModel.JobNo), true);
Dictionary<object, IList<string>> errorMsg = result.Data as Dictionary<object, IList<string>>;
Assert.IsTrue(
(!result.Success)
&& errorMsg.ContainsKey(jobNo)
&& errorMsg[jobNo][0] == "年龄 is required."
&& errorMsg[jobNo][1] == "Code is 必填."
);
}
}
}
附录
更多详细的单元测试信息 请查看 Xxx.BI.Validate 项目中 Xxx.BI.Validate.NUnit 里边详细的单元测试.