您的位置:首页 > 其它

利用特性(Attribute)对实体类进行验证

2010-08-24 00:07 148 查看
最近在做一个EDI项目,主要流程就是客户以HttpPost或Webservice的方式向我们公司下订单,订单内容是以XML格式表示,我这边需要做的操作是:

一: 验证请求是否合法(双方密钥)

二: 验证请求内容是否正确且符合一定的格式要求

三: 对订单进行处理

验证用户的请求是否合法以及对订单的处理就不说了,我今天主要说的是一种优雅、美观、清爽、干净的验证方式


对于XML的内容,我这边的处理方式是将它反序列化成实体对象,毕竟操作一个实体对象比一大堆的XPath强多了。

.net framework自带的XML序列化和反序列化类System.Xml.Serialization.XmlSerializer由于内部实现过于复杂,导致性能不佳 。 我这边自己实现了一个XML反序列化类,性能虽好但比较有针对性,所以今天还是以.net framework自带的XML反序列化类作为示范。

比如说一个XML的内容是这样:

<? xml version = "1.0" encoding = "utf-8"?>

<OrderRequest>

<!-- 订单号 -->

<OrderNo>T-1234567</OrderNo>

<!-- 商品名称 -->

<CommodityName>笔记本电脑</CommodityName>

<!-- 商品数量 -->

<CommodityAmount>1</CommodityAmount>

<!-- 商品重量 单位:KG -->

<CommodityWeight>5.27</CommodityWeight>

<!-- 商品价格 -->

<CommodityValue>13999.00</CommodityValue>

<!-- 希望到达时间 -->

<HopeArriveTime>2010-09-01 00:00:00</HopeArriveTime>

<!-- 结算方式 只能为现结、到付和月结三种 -->

<PayMent>现结</PayMent>

<!-- 备注 -->

<Remark>小心轻放</Remark>

</OrderRequest>

当然,正常的订单不会只有这么点内容,下面我们要为它设计一个实体类:

/// <summary>

/// 订单实体类

/// </summary>

public class OrderRequest

{

/// <summary>

/// 订单号

/// </summary>

public string OrderNo { get; set; }

/// <summary>

/// 商品名称

/// </summary>

public string CommodityName { get; set; }

/// <summary>

/// 商品数量

/// </summary>

public string CommodityAmount { get; set; }

/// <summary>

/// 商品重量

/// </summary>

public string CommodityWeight { get; set; }

/// <summary>

/// 商品价格

/// </summary>

public string CommodityValue { get; set; }

/// <summary>

/// 希望到货时间

/// </summary>

public string HopeArriveTime { get; set; }

/// <summary>

/// 结算方式

/// </summary>

public string PayMent { get; set;}

/// <summary>

/// 备注

/// </summary>

public string Remark { get; set; }

}

可能有的朋友会说,你这也忒不专业了,所有类型都是string。别着急,我想说的重点是验证方式,类型转换之类的先放一边吧。

然后利用微软自带的XML反序列化类将XML反序列成对象,下面是反序列化的方法:

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

/// <summary>

/// 反序列化类

/// </summary>

public class XmlAntiSerialization

{

/// <summary>

/// 反序列化

/// </summary>

/// <param name="type">实体对象类型</param>

/// <param name="xml">XML字符串</param>

/// <returns></returns>

public static object Deserialize(Type type, string xml)

{

try

{

using (StringReader sr = new StringReader(xml))

{

XmlSerializer xmldes = new XmlSerializer(type);

return xmldes.Deserialize(sr);

}

}

catch(Exception ex)

{

throw ex;

}

}

}

只需如此调用即可将XML内容反序列化:

OrderRequest orderRequest = XmlAntiSerialization.Deserialize(typeof(OrderRequest), this.XmlString) as OrderRequest;

传统的验证方式大概是这样的:

if (string.IsNullOrEmpty(orderRequest.OrderNo))

return "订单号不能为空";

if(string.IsNullOrEmpty(orderRequest.CommodityAmount) || !System.Text.RegularExpressions.Regex.IsMatch(orderRequest.CommodityAmount,@"^\d+$"))

return "商品数量不能为空且只能为数字";

if (string.IsNullOrEmpty(orderRequest.PayMent) || (orderRequest.PayMent != "现结" && orderRequest.PayMent != "到付" && orderRequest.PayMent != "月结"))

return "结算方式不能为空,且必须为现结、到付或月结的一种";

如此这般一大堆,想保证页面的清爽是不大可能了,按老赵的话说就是一股语法噪音。但C sharp 这把锋利的刃还给我们提供了更趁手的武器:特性(Attribute)

好吧,让我们先定义一个验证方式的枚举:

/// <summary>

/// 验证类型

/// </summary>

[Flags]

public enum ValidateType

{

/// <summary>

/// 字段或属性是否为空字串

/// </summary>

IsEmpty = 0x0001,

/// <summary>

/// 字段或属性的最小长度

/// </summary>

MinLength = 0x0002,

/// <summary>

/// 字段或属性的最大长度

/// </summary>

MaxLength = 0x0004,

/// <summary>

/// 字段或属性的值是否为数值型

/// </summary>

IsNumber = 0x0008,

/// <summary>

/// 字段或属性的值是否为时间类型

/// </summary>

IsDateTime = 0x0010,

/// <summary>

/// 字段或属性的值是否为正确的浮点类型

/// </summary>

IsDecimal = 0x0020,

/// <summary>

/// 字段或属性的值是否包含在指定的数据源数组中

/// </summary>

IsInCustomArray = 0x0040,

/// <summary>

/// 字段或属性的值是否为固定电话号码格式

/// </summary>

IsTelphone = 0x0080,

/// <summary>

/// 字段或属性的值是否为手机号码格式

/// </summary>

IsMobile = 0x0100

}

再实现一个自定义的特性类:

/// <summary>

/// 为元素添加验证信息的特性类

/// </summary>

[AttributeUsage(AttributeTargets.All)]

public class ValidateAttribute : Attribute

{

/// <summary>

/// 验证类型

/// </summary>

private ValidateType _validateType;

/// <summary>

/// 最小长度

/// </summary>

private int _minLength;

/// <summary>

/// 最大长度

/// </summary>

private int _maxLength;

/// <summary>

/// 自定义数据源

/// </summary>

private string[] _customArray;

/// <summary>

/// 验证类型

/// </summary>

public ValidateType ValidateType

{

get { return this._validateType; }

}

/// <summary>

/// 最小长度

/// </summary>

public int MinLength

{

get { return this._minLength; }

set { this._minLength = value; }

}

/// <summary>

/// 最大长度

/// </summary>

public int MaxLength

{

get { return this._maxLength; }

set { this._maxLength = value; }

}

/// <summary>

/// 自定义数据源

/// </summary>

public string[] CustomArray

{

get { return this._customArray; }

set { this._customArray = value; }

}

/// <summary>

/// 指定采取何种验证方式来验证元素的有效性

/// </summary>

/// <param name="validateType"></param>

public ValidateAttribute(ValidateType validateType)

{

this._validateType = validateType;

}

}

下面我们就可以在实体类的属性上增加特性验证:

public class OrderRequest

{

/// <summary>

/// 订单号

/// </summary>

[Validate(ValidateType.IsEmpty)]

public string OrderNo { get; set; }

/// <summary>

/// 商品名称

/// </summary>

[Validate(ValidateType.IsEmpty|ValidateType.MaxLength,MaxLength = 50)]

public string CommodityName { get; set; }

/// <summary>

/// 商品数量

/// </summary>

[Validate(ValidateType.IsEmpty|ValidateType.IsNumber)]

public string CommodityAmount { get; set; }

/// <summary>

/// 商品重量

/// </summary>

[Validate(ValidateType.IsEmpty | ValidateType.IsDecimal)]

public string CommodityWeight { get; set; }

/// <summary>

/// 商品价格

/// </summary>

[Validate(ValidateType.IsEmpty | ValidateType.IsDecimal)]

public string CommodityValue { get; set; }

/// <summary>

/// 希望到货时间

/// </summary>

[Validate(ValidateType.IsEmpty | ValidateType.IsDateTime)]

public string HopeArriveTime { get; set; }

/// <summary>

/// 结算方式

/// </summary>

[Validate(ValidateType.IsEmpty | ValidateType.IsInCustomArray,CustomArray = new string[]{"现结","到付","月结"})]

public string PayMent { get; set;}

/// <summary>

/// 备注

/// </summary>

[Validate(ValidateType.MaxLength,MaxLength = 256)]

public string Remark { get; set; }

}

由于我们的枚举实用了位标记(FlagsAttribute),所以我们可以对某个元素使用多种验证方式。下面就是验证的实现:

/// <summary>

/// 验证实体对象的所有带验证特性的元素 并返回验证结果 如果返回结果为String.Empty 则说明元素符合验证要求

/// </summary>

/// <param name="entityObject">实体对象</param>

/// <returns></returns>

public static string GetValidateResult(object entityObject)

{

if (entityObject == null)

throw new ArgumentNullException("entityObject");

Type type = entityObject.GetType();

PropertyInfo[] properties = type.GetProperties();

string validateResult = string.Empty;

foreach (PropertyInfo property in properties)

{

//获取验证特性

object[] validateContent = property.GetCustomAttributes(typeof(ValidateAttribute), true);

if (validateContent != null)

{

//获取属性的值

object value = property.GetValue(entityObject, null);

foreach (ValidateAttribute validateAttribute in validateContent)

{

switch (validateAttribute.ValidateType)

{

//验证元素是否为空字串

case ValidateType.IsEmpty:

if (null == value || value.ToString().Length < 1)

validateResult = string.Format("元素 {0} 不能为空", property.Name);

break;

//验证元素的长度是否小于指定最小长度

case ValidateType.MinLength:

if (null == value || value.ToString().Length < 1) break;

if (value.ToString().Length < validateAttribute.MinLength)

validateResult = string.Format("元素 {0} 的长度不能小于 {1}", property.Name, validateAttribute.MinLength);

break;

//验证元素的长度是否大于指定最大长度

case ValidateType.MaxLength:

if (null == value || value.ToString().Length < 1) break;

if (value.ToString().Length > validateAttribute.MaxLength)

validateResult = string.Format("元素 {0} 的长度不能大于{1}", property.Name, validateAttribute.MaxLength);

break;

//验证元素的长度是否符合指定的最大长度和最小长度的范围

case ValidateType.MinLength | ValidateType.MaxLength:

if (null == value || value.ToString().Length < 1) break;

if (value.ToString().Length > validateAttribute.MaxLength || value.ToString().Length < validateAttribute.MinLength)

validateResult = string.Format("元素 {0} 不符合指定的最小长度和最大长度的范围,应该在 {1} 与 {2} 之间", property.Name, validateAttribute.MinLength, validateAttribute.MaxLength);

break;

//验证元素的值是否为值类型

case ValidateType.IsNumber:

if (null == value || value.ToString().Length < 1) break;

if (!System.Text.RegularExpressions.Regex.IsMatch(value.ToString(), @"^\d+$"))

validateResult = string.Format("元素 {0} 的值不是值类型", property.Name);

break;

//验证元素的值是否为正确的时间格式

case ValidateType.IsDateTime:

if (null == value || value.ToString().Length < 1) break;

if (!System.Text.RegularExpressions.Regex.IsMatch(value.ToString(), @"(\d{2,4})[-/]?([0]?[1-9]|[1][12])[-/]?([0][1-9]|[12]\d|[3][01])\s*([01]\d|[2][0-4])?[:]?([012345]?\d)?[:]?([012345]?\d)?"))

validateResult = string.Format("元素 {0} 不是正确的时间格式", property.Name);

break;

//验证元素的值是否为正确的浮点格式

case ValidateType.IsDecimal:

if (null == value || value.ToString().Length < 1) break;

if (!System.Text.RegularExpressions.Regex.IsMatch(value.ToString(), @"^\d+[.]?\d+$"))

validateResult = string.Format("元素 {0} 不是正确的金额格式", property.Name);

break;

//验证元素的值是否在指定的数据源中

case ValidateType.IsInCustomArray:

if (null == value || value.ToString().Length < 1) break;

if (null == validateAttribute.CustomArray || validateAttribute.CustomArray.Length < 1)

validateResult = string.Format("系统内部错误:元素 {0} 指定的数据源为空或没有数据", property.Name);

bool isHas = Array.Exists<string>(validateAttribute.CustomArray, delegate(string str)

{

return str == value.ToString();

}

);

if (!isHas)

validateResult = string.Format("元素 {0} 的值设定不正确 , 应该为 {1} 中的一种", property.Name, string.Join(",", validateAttribute.CustomArray));

break;

//验证元素的值是否为固定电话号码格式

case ValidateType.IsTelphone:

if (null == value || value.ToString().Length < 1) break;

if (!System.Text.RegularExpressions.Regex.IsMatch(value.ToString(), @"^(\d{3,4}-)?\d{6,8}$"))

validateResult = string.Format("元素 {0} 不是正确的固定电话号码格式", property.Name);

break;

//验证元素的值是否为手机号码格式

case ValidateType.IsMobile:

if (null == value || value.ToString().Length < 1) break;

if (!System.Text.RegularExpressions.Regex.IsMatch(value.ToString(), @"^[1]+[3,5]+\d{9}$"))

validateResult = string.Format("元素 {0} 不是正确的手机号码格式", property.Name);

break;

//验证元素是否为空且符合指定的最小长度

case ValidateType.IsEmpty | ValidateType.MinLength:

if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;

goto case ValidateType.MinLength;

//验证元素是否为空且符合指定的最大长度

case ValidateType.IsEmpty | ValidateType.MaxLength:

if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;

goto case ValidateType.MaxLength;

//验证元素是否为空且符合指定的长度范围

case ValidateType.IsEmpty | ValidateType.MinLength | ValidateType.MaxLength:

if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;

goto case ValidateType.MinLength | ValidateType.MaxLength;

//验证元素是否为空且值为数值型

case ValidateType.IsEmpty | ValidateType.IsNumber:

if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;

goto case ValidateType.IsNumber;

//验证元素是否为空且值为浮点型

case ValidateType.IsEmpty | ValidateType.IsDecimal:

if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;

goto case ValidateType.IsDecimal;

//验证元素是否为空且值为时间类型

case ValidateType.IsEmpty | ValidateType.IsDateTime:

if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;

goto case ValidateType.IsDateTime;

//验证元素是否为空且值在指定的数据源中

case ValidateType.IsEmpty | ValidateType.IsInCustomArray:

if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;

goto case ValidateType.IsInCustomArray;

//验证元素是否为空且值为固定电话号码格式

case ValidateType.IsEmpty | ValidateType.IsTelphone:

if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;

goto case ValidateType.IsTelphone;

//验证元素是否为空且值为手机号码格式

case ValidateType.IsEmpty | ValidateType.IsMobile:

if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;

goto case ValidateType.IsMobile;

default:

break;

}

}

}

if (!string.IsNullOrEmpty(validateResult))

break;

}

return validateResult;

}

最后,我们只需调用这么一句代码,就可以实现对整个实体类的元素的验证:

//验证订单

string checkMessage = AttributeHandle.GetValidateResult(orderRequest);

if(!string.IsNullOrEmpty(checkMessage))

return checkMessage;

//do something....

大功告成,整个页面清爽无比


附:由于公司的一些原因,我只能在基于.net framework2.0的VS2005上开发,是故后面的实现代码有些冗长


希望大家多提意见或建议,共同学习,共同进步!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐