基于.Net Attribute 应用的自动验证
由于公司的需要,从去年开始,我就从Java转向Net平台进行开发了,说实话,刚开始接触Net的时候还是很不习惯,最不习惯的是对多态的处理,经常忘记将某些方法声明为Virtual,尤其在习惯了Java的抽象概念之后,在实际应用中,犯了不少错误,不过在浏览了许多大侠的博客后,对C#语言的特性有了进一步的了解,特别钟情于它的Attribute特性,这种在运行期间感知代码级别的能力与Java中的元数据很相似,并且更容易使用,因此在一次公司的基于 Net 接口程序改造中,我尝试使用这种机制简化验证信息的开发。
由于我们公司是一个数据库公司,卖的是实时数据库,与SQL,ACCESS等概念不同,讲究的是数据的压缩率以及存储速度,因此在与各种设备进行数据转换时,没有标准的如SQL等通讯协议可用,所以公司每个项目,基本上都需要增对数据源(SQL,Access,PLC)等设备开发接口程序,这样一来,随着项目的增对,程序的代码和维护变得很困难,在一次客户增对接口程序的投诉后,公司决定对通讯接口进行统一的开发,这次改造要求将代码的编写量减少,并且要求所有程序拥有一致的界面,一致的缓存机制,开发人员只需要开发特定于设备的数据采集代码,这样就避免了代码质量的参差不齐,同时减少了不必要的重复代码的开发。
这个工作很荣幸(其实很不幸)的落到了我的头上,在我仔细查看了客户针对程序的投诉后,发现问题居然是由于没有正确对配置参数进行验证造成的,客户在一个需要输入IP的位置输入了错误格式的IP地址,居然造成了程序的崩溃,这也从侧面显示出了现在的许多大学刚毕业出来的程序员对程序的分析不严谨,许多开发人员往往认为客户都和他们一样聪明,都会将信息输入准确无误,然后程序运行稳定,他们就可以高枕无忧准备去夏威夷度假了,殊不知这种观念正式造成他们节日狂欢被迫取消的原因(奖金被扣),在分析过后,我决定首先对验证信息这一块下手
在对配置文件分析后,我将这个模块定义如下
1. 需要将配置信息分为两部分,必须的(Required)和可选的(Option)
2. 由于配置信息的多样,因此不可能实现所有的验证,因此要让开发人员有能力自定义参数的验证,例如验证一个PLC设备名称是否符合要求
使用Net的Attribute机制,编写UML图如下,验证信息栏提供了基本的参数验证
我将参数分为了RequiredParam和OptionParam两部分,同时让他们都继承于同一个类ParamAttribute,ParamAttribute代码如下
ParamTypeEnum 枚举代码如下
RequiredParamAttribute以及OptionParamAttribute 中的所有特性均继承至ParamAttribute中,他们仅提供标识属性的类型的作用
RequiredParamAttribute代码如下
由于我们公司是一个数据库公司,卖的是实时数据库,与SQL,ACCESS等概念不同,讲究的是数据的压缩率以及存储速度,因此在与各种设备进行数据转换时,没有标准的如SQL等通讯协议可用,所以公司每个项目,基本上都需要增对数据源(SQL,Access,PLC)等设备开发接口程序,这样一来,随着项目的增对,程序的代码和维护变得很困难,在一次客户增对接口程序的投诉后,公司决定对通讯接口进行统一的开发,这次改造要求将代码的编写量减少,并且要求所有程序拥有一致的界面,一致的缓存机制,开发人员只需要开发特定于设备的数据采集代码,这样就避免了代码质量的参差不齐,同时减少了不必要的重复代码的开发。
这个工作很荣幸(其实很不幸)的落到了我的头上,在我仔细查看了客户针对程序的投诉后,发现问题居然是由于没有正确对配置参数进行验证造成的,客户在一个需要输入IP的位置输入了错误格式的IP地址,居然造成了程序的崩溃,这也从侧面显示出了现在的许多大学刚毕业出来的程序员对程序的分析不严谨,许多开发人员往往认为客户都和他们一样聪明,都会将信息输入准确无误,然后程序运行稳定,他们就可以高枕无忧准备去夏威夷度假了,殊不知这种观念正式造成他们节日狂欢被迫取消的原因(奖金被扣),在分析过后,我决定首先对验证信息这一块下手
在对配置文件分析后,我将这个模块定义如下
1. 需要将配置信息分为两部分,必须的(Required)和可选的(Option)
2. 由于配置信息的多样,因此不可能实现所有的验证,因此要让开发人员有能力自定义参数的验证,例如验证一个PLC设备名称是否符合要求
使用Net的Attribute机制,编写UML图如下,验证信息栏提供了基本的参数验证
我将参数分为了RequiredParam和OptionParam两部分,同时让他们都继承于同一个类ParamAttribute,ParamAttribute代码如下
/// <summary>
/// 标识一个组件需要配置的属性
/// </summary>
[UtilityLibrary.Developer.SoftwareAttribute.AuthorAttribute("Vincent", "Developer", "2008-5-28")]
[System.AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class ParamAttribute : System.Attribute
{
private String m_paramName;
/// <summary>
/// 标识参数的名称
/// </summary>
public String ParamName
{
get { return m_paramName; }
}
private ParamTypeEnum m_paramType;
/// <summary>
/// 表示此参数的类型,提供编译器调用对应的检验函数
/// </summary>
public ParamTypeEnum ParamType
{
get { return m_paramType; }
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="paramName">参数的名称</param>
/// <param name="paramType">参数的类型</param>
public ParamAttribute(String paramName, ParamTypeEnum paramType)
{
m_paramName = paramName;
m_paramType = paramType;
}
}
/// 标识一个组件需要配置的属性
/// </summary>
[UtilityLibrary.Developer.SoftwareAttribute.AuthorAttribute("Vincent", "Developer", "2008-5-28")]
[System.AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class ParamAttribute : System.Attribute
{
private String m_paramName;
/// <summary>
/// 标识参数的名称
/// </summary>
public String ParamName
{
get { return m_paramName; }
}
private ParamTypeEnum m_paramType;
/// <summary>
/// 表示此参数的类型,提供编译器调用对应的检验函数
/// </summary>
public ParamTypeEnum ParamType
{
get { return m_paramType; }
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="paramName">参数的名称</param>
/// <param name="paramType">参数的类型</param>
public ParamAttribute(String paramName, ParamTypeEnum paramType)
{
m_paramName = paramName;
m_paramType = paramType;
}
}
ParamTypeEnum 枚举代码如下
/// <summary>
/// 标识参数的类型枚举
/// </summary>
[Flags()]
public enum ParamTypeEnum
{
Integer,
Double,
String,
IP,
Port,
UInt,
File,
Boolean,
DnaService,
UserDefined = -1
}
/// 标识参数的类型枚举
/// </summary>
[Flags()]
public enum ParamTypeEnum
{
Integer,
Double,
String,
IP,
Port,
UInt,
File,
Boolean,
DnaService,
UserDefined = -1
}
RequiredParamAttribute以及OptionParamAttribute 中的所有特性均继承至ParamAttribute中,他们仅提供标识属性的类型的作用
RequiredParamAttribute代码如下
/// <summary>
/// 标识一个类要正常运行所必需要的属性,提供编译器执行相应的验证函数或用户自定义的验证函数
/// </summary>
[UtilityLibrary.Developer.SoftwareAttribute.AuthorAttribute("Vincent", "Developer", "2008-5-28")]
[System.AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class RequiredParamAttribute : ParamAttribute
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="paramName">参数的名称</param>
/// <param name="paramType">参数的类型</param>
public RequiredParamAttribute(String paramName, ParamTypeEnum paramType)
: base(paramName,paramType)
{
}
}
/// 标识一个类要正常运行所必需要的属性,提供编译器执行相应的验证函数或用户自定义的验证函数
/// </summary>
[UtilityLibrary.Developer.SoftwareAttribute.AuthorAttribute("Vincent", "Developer", "2008-5-28")]
[System.AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class RequiredParamAttribute : ParamAttribute
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="paramName">参数的名称</param>
/// <param name="paramType">参数的类型</param>
public RequiredParamAttribute(String paramName, ParamTypeEnum paramType)
: base(paramName,paramType)
{
}
}
OptionParamAttribute 代码如下
/// <summary>
/// 标识一个组件的可选属性
/// </summary>
[System.AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class OptionParamAttribute : ParamAttribute
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="paramName">参数的名称</param>
/// <param name="paramType">参数的类型</param>
public OptionParamAttribute(String paramName, ParamTypeEnum paramType)
: base(paramName, paramType)
{
}
}
/// 标识一个组件的可选属性
/// </summary>
[System.AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class OptionParamAttribute : ParamAttribute
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="paramName">参数的名称</param>
/// <param name="paramType">参数的类型</param>
public OptionParamAttribute(String paramName, ParamTypeEnum paramType)
: base(paramName, paramType)
{
}
}
同时,定义一个ICustomerValidator的接口,当开发人员实现这个接口时,就可以实现自定义验证机制,代码如下
/// <summary>
/// 提供自定义验证信息的功能
/// </summary>
public interface ICustomerValidator
{
bool Validator(String paramName, String value);
}
/// 提供自定义验证信息的功能
/// </summary>
public interface ICustomerValidator
{
bool Validator(String paramName, String value);
}
完成后,编写一个Validator类,捕获配置了Params属性的信息
/// <summary>
/// 获取设置了Params类的信息
/// </summary>
public class Validator
{
private ICustomerValidator m_customerValidator;
private ParamAttribute[] m_params;
private RequiredParamAttribute[] m_requriedParams;
public RequiredParamAttribute[] RequriedParams
{
get { return m_requriedParams; }
}
private OptionParamAttribute[] m_optionParams;
public OptionParamAttribute[] OptionParams
{
get { return m_optionParams; }
}
private Dictionary<String, RequiredParamAttribute> m_requiredDictionary = new Dictionary<String,RequiredParamAttribute>();
private Dictionary<String, ParamAttribute> m_paramDictionary = new Dictionary<String, ParamAttribute>();
public Validator(Object o)
{
if (o == null)
throw new ArgumentNullException();
//获取o的类型
Type t = o.GetType();
//判断o是否实现了自定义验证信息
if (TypeUtility.IsType<ICustomerValidator>(t))
m_customerValidator = o as ICustomerValidator;
m_params = AttributeUtility.GetCustomAttributes<ParamAttribute>(t, true);
m_requriedParams = AttributeUtility.GetCustomAttributes<RequiredParamAttribute>(t, true);
m_optionParams = AttributeUtility.GetCustomAttributes<OptionParamAttribute>(t, true);
InitDictionary();
}
/// <summary>
/// 初始化所有信息的字典列表
/// </summary>
private void InitDictionary()
{
foreach (ParamAttribute p in m_params)
{
if (!m_paramDictionary.ContainsKey(p.ParamName))
{
m_paramDictionary.Add(p.ParamName, p);
}
}
foreach (RequiredParamAttribute p in m_requriedParams)
{
if (!m_requiredDictionary.ContainsKey(p.ParamName))
{
m_requiredDictionary.Add(p.ParamName, p);
}
}
}
/// <summary>
/// 验证某个参数设置的值是否正确
/// </summary>
/// <param name="paramName">参数的名称</param>
/// <param name="paramValue">参数的值</param>
/// <returns>正确 : true 错误 : false</returns>
public bool Validate(String paramName,String paramValue)
{
if (m_requiredDictionary.ContainsKey(paramName))
{
RequiredParamAttribute p = m_requiredDictionary[paramName];
return ParamValidators.Validator(m_customerValidator, paramName, paramValue, p.ParamType);
}
return false;
}
}
/// 获取设置了Params类的信息
/// </summary>
public class Validator
{
private ICustomerValidator m_customerValidator;
private ParamAttribute[] m_params;
private RequiredParamAttribute[] m_requriedParams;
public RequiredParamAttribute[] RequriedParams
{
get { return m_requriedParams; }
}
private OptionParamAttribute[] m_optionParams;
public OptionParamAttribute[] OptionParams
{
get { return m_optionParams; }
}
private Dictionary<String, RequiredParamAttribute> m_requiredDictionary = new Dictionary<String,RequiredParamAttribute>();
private Dictionary<String, ParamAttribute> m_paramDictionary = new Dictionary<String, ParamAttribute>();
public Validator(Object o)
{
if (o == null)
throw new ArgumentNullException();
//获取o的类型
Type t = o.GetType();
//判断o是否实现了自定义验证信息
if (TypeUtility.IsType<ICustomerValidator>(t))
m_customerValidator = o as ICustomerValidator;
m_params = AttributeUtility.GetCustomAttributes<ParamAttribute>(t, true);
m_requriedParams = AttributeUtility.GetCustomAttributes<RequiredParamAttribute>(t, true);
m_optionParams = AttributeUtility.GetCustomAttributes<OptionParamAttribute>(t, true);
InitDictionary();
}
/// <summary>
/// 初始化所有信息的字典列表
/// </summary>
private void InitDictionary()
{
foreach (ParamAttribute p in m_params)
{
if (!m_paramDictionary.ContainsKey(p.ParamName))
{
m_paramDictionary.Add(p.ParamName, p);
}
}
foreach (RequiredParamAttribute p in m_requriedParams)
{
if (!m_requiredDictionary.ContainsKey(p.ParamName))
{
m_requiredDictionary.Add(p.ParamName, p);
}
}
}
/// <summary>
/// 验证某个参数设置的值是否正确
/// </summary>
/// <param name="paramName">参数的名称</param>
/// <param name="paramValue">参数的值</param>
/// <returns>正确 : true 错误 : false</returns>
public bool Validate(String paramName,String paramValue)
{
if (m_requiredDictionary.ContainsKey(paramName))
{
RequiredParamAttribute p = m_requiredDictionary[paramName];
return ParamValidators.Validator(m_customerValidator, paramName, paramValue, p.ParamType);
}
return false;
}
}
同时,开发一个参数配置页面如下
窗体代码如下
public partial class Form1 : Form
{
private UtilityLibrary.Developer.ParamValidate.Validator m_validator;
public Form1(Object o)
{
InitializeComponent();
m_validator = new UtilityLibrary.Developer.ParamValidate.Validator(o);
InitParamGridView();
}
/// <summary>
/// 加载所有配置参数信息
/// </summary>
private void InitParamGridView()
{
RequiredParamAttribute[] requriedParams = m_validator.RequriedParams;
if (requriedParams != null && requriedParams.Length != 0)
{
for (int i = 0; i < requriedParams.Length; i++)
{
DataGridViewRow newRow = new DataGridViewRow();
newRow.CreateCells(dataGridView1);
newRow.Cells[0].Style.ForeColor = Color.Red;
newRow.Cells[0].Value = requriedParams[i].ParamName;
newRow.Cells[1].Value = ParamTypeHelper.GetTypeDesc(requriedParams[i].ParamType);
this.dataGridView1.Rows.Add(newRow);
}
}
OptionParamAttribute[] optionParams = m_validator.OptionParams;
if (optionParams != null && optionParams.Length != 0)
{
for (int i = 0; i < optionParams.Length; i++)
{
DataGridViewRow newRow = new DataGridViewRow();
newRow.CreateCells(dataGridView1);
newRow.Cells[0].Value = optionParams[i].ParamName;
newRow.Cells[1].Value = ParamTypeHelper.GetTypeDesc(optionParams[i].ParamType);
this.dataGridView1.Rows.Add(newRow);
}
}
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
// ColumnIndex为3代表点击的是单击事件
if (e.ColumnIndex == 3)
{
String paramName = dataGridView1.Rows[e.RowIndex].Cells[0].Value.ToString();
object paramValue = dataGridView1.Rows[e.RowIndex].Cells[2].Value;
m_validator.Validate(paramName, paramValue == null ? "" : paramValue.ToString());
}
}
}
{
private UtilityLibrary.Developer.ParamValidate.Validator m_validator;
public Form1(Object o)
{
InitializeComponent();
m_validator = new UtilityLibrary.Developer.ParamValidate.Validator(o);
InitParamGridView();
}
/// <summary>
/// 加载所有配置参数信息
/// </summary>
private void InitParamGridView()
{
RequiredParamAttribute[] requriedParams = m_validator.RequriedParams;
if (requriedParams != null && requriedParams.Length != 0)
{
for (int i = 0; i < requriedParams.Length; i++)
{
DataGridViewRow newRow = new DataGridViewRow();
newRow.CreateCells(dataGridView1);
newRow.Cells[0].Style.ForeColor = Color.Red;
newRow.Cells[0].Value = requriedParams[i].ParamName;
newRow.Cells[1].Value = ParamTypeHelper.GetTypeDesc(requriedParams[i].ParamType);
this.dataGridView1.Rows.Add(newRow);
}
}
OptionParamAttribute[] optionParams = m_validator.OptionParams;
if (optionParams != null && optionParams.Length != 0)
{
for (int i = 0; i < optionParams.Length; i++)
{
DataGridViewRow newRow = new DataGridViewRow();
newRow.CreateCells(dataGridView1);
newRow.Cells[0].Value = optionParams[i].ParamName;
newRow.Cells[1].Value = ParamTypeHelper.GetTypeDesc(optionParams[i].ParamType);
this.dataGridView1.Rows.Add(newRow);
}
}
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
// ColumnIndex为3代表点击的是单击事件
if (e.ColumnIndex == 3)
{
String paramName = dataGridView1.Rows[e.RowIndex].Cells[0].Value.ToString();
object paramValue = dataGridView1.Rows[e.RowIndex].Cells[2].Value;
m_validator.Validate(paramName, paramValue == null ? "" : paramValue.ToString());
}
}
}
在测试中,添加一个类Test如下
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1(new Test()));
}
}
[RequiredParam("IP地址",ParamTypeEnum.IP)]
[RequiredParam("端口号",ParamTypeEnum.Port)]
[OptionParam("可选参数",ParamTypeEnum.String)]
[RequiredParam("自定义验证",ParamTypeEnum.UserDefined)]
public class Test : ICustomerValidator
{
ICustomerValidator 成员
}
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1(new Test()));
}
}
[RequiredParam("IP地址",ParamTypeEnum.IP)]
[RequiredParam("端口号",ParamTypeEnum.Port)]
[OptionParam("可选参数",ParamTypeEnum.String)]
[RequiredParam("自定义验证",ParamTypeEnum.UserDefined)]
public class Test : ICustomerValidator
{
ICustomerValidator 成员
}
运行窗体后,显示界面如下
红色为系统运行时必须的参数,点击Ip地址的验证按钮,将执行系统自带的验证函数
同时点击自定义验证函数时,信息显示如下
由于小弟对Net平台知识很欠缺,如果有什么地方做得不好,希望各位前辈多指点,谢谢。