浅谈反射与特性在接口系统中的应用(编码表转化)
问题描述:
业务系统A和B需要共享数据,以达到数据利用最大化,减少工作人员的录入数据的工作量。说明一下,业务系统A和B各自使用不同的编码表,简单的说,在业务系统A中,性别是使用01和02代表男和女,而在业务系统B中,性别则采用了m和w表示男和女。现在,这两个系统要共享性别这个数据项,所以我们需要做的必不可少的工作就是:编码表的转化。这个问题有很高的普遍性,基本上只要是业务系统之间共享数据,那么这一步是必不可少的。今天,我就对此问题谈一谈我的解决之道,供大家探讨。
解决思路:
1) 我们最容易想到的办法就是,编写一个方法,针对输入的编码进行匹配转化,如一下代码所示:
{
string result = string.Empty;
switch(code.Trim())
{
case "01":
result = "m";
break;
case "02":
result = "w";
break;
}
return result;
}
然后,在外部进行调用此方法即可。如果我们的编码表转化数量比较少的话,这一种方法,显然已经可以满足要求了。但假如我们有很多的编码表需要转化,这一种方法使用起来应该是很吃力的。而且,一旦数据项的编码之间对应关系一旦发生变化,我们就必须要改写程序,然后编译、发布。显然此方法的灵活性还不太好。那么我们就需要寻找另一种灵活的,可以应对编码对应关系可能会变的方法。
1) 使用数据库或者xml存储编码表之间的对应关系。使用数据库也好,xml也好,都是把问题的可变性单独分离出来,以数据库存储为例,可以设计如下表结构(脚本如下):
(
id int identity(1,1) primary key,
codeType varchar(10), --编码的类型
codeA varchar(10), --业务系统A的编码
codeA_Name varchar(20), --业务系统A的对应编码的描述
codeB varchar(10),--业务系统B的编码
codeB_Name varchar(20) --业务系统B的对应编码的描述
)
在这个表里存储编码之间的映射关系。现在,如果你需要转化,那么你就需要查询这个表,就可以得到转化后的编码。而且一旦编码之间的对应关系变了,那么你只需要修改这个表里的数据即可完成修改,相对上一种方法,已经增加抗变性。再回头看,抗变性是增加了,可是客户程序使用的时候仍然比较麻烦,仍然需要关心,哪个编码需要转化。其实,从职责分配的角度讲,哪个编码需要转化,是在使用之前就可以确定下来的事情,所以我们可不可以让编码自动转化呢?如果可以的话,那么客户程序就不再需要关心哪个编码需要转化的事情啦,毕竟这是一件烦人的事情。现在,我们整理一下思路,假设有一个负责转化的方法,如下:
{}
那么,目前如果有一个实体类Person需要转化编码的时候,需要调用:
p.SexCode = ConvertCode("CodeType",p.SexCode);
p.MarriageStatusCode = ConvertCode("CodeType",p.MarriageStatusCode);
可以看出,我们需要知道哪一个属性的编码需要转化。那么,我们希望编码可以自动转化,使用编码转化时,不再关心哪一个属性的编码需要转化,代码如下:
p = ConvertObjectCode(p); //一次性把所有需要转化的编码全部转化。
这样的代码看起来会比较舒服,客户代码只需要关心何时转化编码,而不关心编码如何转化,这就是职责的分离。
我们应该怎样编写ConvertObjectCode(object convert_obj)方法呢?下面就重点介绍使用反射和特性(自定义属性)就可以做到。
为了更好的提高方法的使用性,我们会给ConvertObjectCode方法添加一个参数,用于指示,编码转化的方向,即:是从业务系统A到B还是B到A。添加这个参数这个方法就可以做到双向转化啦。
让我们继续探讨ConvertObjectCode方法的功能吧。在这里,这个方法需要做的事情是:转化类的属性编码,但要转化的结果则归属于类的属性,因为每个属性转化后的结果是不一样的,而要转化为什么,类的属性是知道的。所以,在这里我们需要再次细化职责,ConvertObjectCode方法只负责转化,而转化为什么则由类的属性承担。看到这里,可能你已经知道如何做啦,那就看一下代码吧。
首先,需要编写一个特性,用于标注哪一个类的属性需要转化,做何种转化。代码如下:
public class CodeConvertAttribute:Attribute
{
public CodeConvertAttribute(string codeType)
{
this.m_codeType = codeType;
}
private string m_codeType;
public string CodeType //编码的类型
{
get
{
return this.m_codeType;
}
set
{
this.m_codeType = value;
}
}
}
其次,在需要做编码转化类的属性上加特性。代码如下:
public string Sex
{
get
{
return m_Sex;
}
set
{
this.m_Sex = value;
}
}
最后,做转化的动作,如下:
/// 自动转化实体类的各个属性的编码(系统A与系统B)
/// </summary>
/// <param name="convertObj">要转化的对象</param>
/// <param name="huNanToLocal">true:A的编码转化为B;false:B的编码转化为A</param>
/// <returns></returns>
public static object AutoConvertCode(object convertObj,bool ToLocal)
{
Type type = convertObj.GetType();
PropertyInfo[] props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach(PropertyInfo p in props)
{
object convertObj_value = p.GetValue(convertObj,null);
string js_type = string.Empty;
object[] customerAttributes = p.GetCustomAttributes(typeof(CodeConvertAttribute),false);
if(customerAttributes.Length > 0)
{
CodeConvertAttribute attr = (CodeConvertAttribute)customerAttributes[0];
js_type = attr.CodeType;
if(ToLocal)
p.SetValue(convertObj,ConvertCode(js_type,convertObj_value.ToString()),null);
else
p.SetValue(convertObj, ConvertCode (js_type,convertObj_value.ToString()),null);
}
}
return convertObj;
}
现在,你就可以轻松的对任意的对象进行编码转化了,如果某个类的属性需要增加编码转化的工作,那么只需要把特性CodeConvert添加到对应的属性上即可,剩下的事情就不用操心啦。怎么样,现在的你还觉得特性和反射遥不可及嘛?
注:之所以写这样的一片post主要是前一段时间,发布了几篇关于反射和特性原理的文章,很多朋友都说应用起来有点困难,这一篇就算是对这两篇文章的补充吧。 另外,今天也是一个值得庆祝的日子,因为我自己的小站:问题小站已经开通
了。这篇post也算是给它的贺礼吧。