前言
反射(Reflection),虽然是很久以前就已经有的概念,且在微软的C#.NET中已经有做好相关的组件System.Reflection
,开发者可以很容易地去应用反射的概念,如果你想做下面的事情的话:
当我们自定义完一个类别(class),你要怎么在执行阶段取得类别的
型别(Type)属性(Attribute)资讯(PropertyInfo、FieldInfo or MemberInfo)是否有挂标籤(CustomAttribute)或者当我们在Model里面定义完一个方法,你要怎么在执行阶段取得方法的
内容(MethodInfo)参数(MethodInfo.GetGenericArguments)回传值内容(MethodInfo.ReturnParamter)回传值型别(MethodInfo.ReturnType)etc...只是不知怎么的,待过的公司专案几乎没有用到这个概念,但它真的很好用,搭配泛型可以把很多东西提取出来共用,可以省下非常多的功夫。
应用于ADO.NET
最明显可以写成共用的就是ADO.NET,以前在使用ADO.NET存取资料库的时候,不外乎就是SqlConnection连线之后,透过SqlCommand下指令,再透过SqlDataAdapter的Fill方法,把资料填入DataTable或是用DataReader来读取资料再塞到Model里面。
DataTable与Model的Mapping
DataTable如果要转换成Model List通常做法会是这样。
//传入DataTable物件,回传Model Listpublic List<DataClass> GetModelList(DataTable dt){ var result = new List<DataClass>(); //一笔一笔透过栏位名称与Model绑定 foreach(var row in dt.Rows) { var element = new DataClass(); element.PropA = row["PropA"].ToString(); element.PropB = row["PropB"].ToString(); //etc.. result.Add(element); } return result;}
相当简单易懂,Datable进来就一个一个绑定到Model里面,非常适合新人来阅读还有撰写,不过写久了就觉得感觉好流水帐,而且element.XXX = row.Column["XXX"].ToString(),这种动作栏位有几个就要重複几次,难道没有更好的做法?有的,请看下方。
public List<DataClass> GetModelList(DataTable dt){ //一行解决 return dt.MapToModelList<DataClass>();}//DataTable与Model List转换//并应用泛型来使所有物件都可以使用这方法public static List<T> MapToModelList<T>(this DataTable dataTable){ var result = new List<T>(); foreach (DataRow dr in dataTable.Rows) { result.Add(dr.MapToModel<T>()); } return result;}//DataRow与Model转换private static T MapToModel<T>(this DataRow dataRow){ //先建立一个预设回传值 var result = Activator.CreateInstance<T>(); //透过Reflection取得型别与属性清单 foreach (var property in typeof(T).GetProperties()) { //透过Reflection绑定值 property.SetValue(result, dataRow[property.Name].ToString()); } return result;}
感觉门槛高了一点,但是其实并不难,而且写完之后可以打通你的任督二脉,之后如果有DataTable要转Model List的,只要写一行就好了。
DataReader与Model的Mapping
类似作法也会出现在DataReader与Model List的转换,比较传统的作法会是这样。
//传入DataReader物件,回传Model Listprivate static List<DataClass> GetModelList(DbDataReader dataReader){ var result = new List<DataClass>(); while (dataReader.Read()) { var element = new DataClass(); element.PropA = dataReader["PropA"].ToString(); element.PropB = dataReader["PropB"].ToString(); //etc... result.Add(element); } return result;}
应用类似的概念,修改成
//DataReader与Model List的转换//并应用泛型来使所有物件都可以使用这方法public List<T> MapToModelList<T>(DbDataReader dataReader){ var result = new List<T>(); while (dataReader.Read()) { result.Add(dataReader.MapToModel<T>()); } return result;}//DataReader与Model的转换private static T MapToModel<T>(this DbDataReader dataReader){ //先建立一个预设回传值 var result = Activator.CreateInstance<T>(); //透过Reflection取得型别与属性清单 foreach (var property in typeof(T).GetProperties()) { //透过Reflection绑定值 property.SetValue(result, dataReader[property.Name]); } return result;}
结语
以上两种做法都只是简单实作一下,实际上直接呼叫这两个方法会出现一些例外,例如资料库栏位名称不正确或者值的型态需要转换等等,我有做了一个小小的套件放在Github上,这套件有解决了某些例外情形,例如属性与栏位型别之间的判定以及如果属性与栏位的名称不同,可以透过Column Attribute来设定资料表栏位的名称,希望提供给各位参考参考。
SimpleObjectMapper
应用反射的技巧,就可以省下很多时间来做更有意义的事情,但其实我这样做的初衷很简单,就是懒
,所以懒惰是人类进步的原动力,这个观点真的是对极了。
参考来源
Reflection
SimpleObjectMapper