IL使用-ORM DataTable数据转换为模型类实例
最近在研究动态程序集,遇到了IL语言,故学习了下,换了一个月的时间做了个DB框架,写的这篇文章呢,主要是想将我做的这个DB框架的一部分,代码分享给大家,这是一个ORM对象关系映射转换器,准确的说是将 DataTable 数据转换为模型类实例。
我之所以会写出这个呢,是因为我之前经常用反射做ORM,发现效率非常低,可是使用了这样的方法,效率会高许多。这个转换器,有个泛型T,由于为该转换器加了反射的缓存,所以第一次使用该T类型的转换,会消耗一个反射出所有属性的时间,第二次就不需要使用反射,也就去掉了反射消耗的时间,提高了效率。而用IL创建出动态方法时,只消耗了小于1毫秒的时间,调用该动态方法则更不用说,远小于1毫秒的时间,比反射调用一次4毫秒要快速很多。
而且这样还有个特点,因为是动态方法,DataTable返回数据列可以变,动态方法会跟着变,突破了限制。
代码如下,先介绍两个特性:
一个是用于说明属性对应哪个列的特性,OutDataMappingAttribute 特性
namespace DBManager.Attributes
{
/// <summary>
/// DataTable列映射属性
/// </summary>
public class OutDataMappingAttribute:Attribute
{
private string columnName;
/// <summary>
/// 获取或设置映射列的名称
/// </summary>
public string ColumnName
{
get { return columnName; }
set { columnName = value; }
}
public OutDataMappingAttribute(string columnName)
{
this.columnName = columnName;
}
}
}
另一个则是,用于处理DBNull类型的DefaultValue特性
namespace DBManager.Attributes
{
/// <summary>
/// 默认值设置特性
/// </summary>
public class DefaultValueAttribute:Attribute
{
private object defaultValue;
public object DefaultValue
{
get { return defaultValue; }
set { defaultValue = value; }
}
public DefaultValueAttribute(object value)
{
this.defaultValue = value;
}
}
}
下面则是转换器的代码,有兴趣的朋友,还可以将创建的动态方法做缓存处理,我这里就不写明了
1 1 using System; 2 2 using System.Collections.Generic; 3 3 using System.Text; 4 4 using System.Data; 5 5 using System.Reflection; 6 6 using System.Reflection.Emit; 7 7 8 8 namespace DBManager 9 9 { 10 10 /// <summary> 11 11 /// 用于将DataTable中的数据转换为模型类的转换器 12 12 /// 如需使用生成的程序集之外的特性,作为映射数据特性和默认值特性 13 13 /// 请遵照生成的程序集中特性布局来,否则会引发错误! 14 14 /// </summary> 15 15 public class ModelConverter 16 16 { 17 17 private const string METHOD_NAME = "ToList"; 18 18 private volatile Dictionary<Type, TypeInfoDictionary> typeInfoDic; 19 19 20 20 /// <summary> 21 21 /// 需注意,不要经常创建该实例,因为其中带有转换用的缓存 22 22 /// 可提高效率,如经常创建,反降低效率 23 23 /// </summary> 24 24 public ModelConverter() 25 25 { 26 26 typeInfoDic = new Dictionary<Type, TypeInfoDictionary>(); 27 27 } 28 28 29 29 30 30 /// <summary> 31 31 /// 将DataTable转换为含有模型类类型的List 32 32 /// </summary> 33 33 /// <typeparam name="T">模型类类型, 34 34 /// 注:如需使用生成的程序集之外的模型类,需要公开的默认构造函数</typeparam> 35 35 /// <param name="table">数据源</param> 36 36 /// <returns>模型类实例的结果集</returns> 37 37 public List<T> ConvertToList<T>(DataTable table) where T:class 38 38 { 39 39 List<T> list = new List<T>(); 40 40 Type modelType = typeof(T); 41 41 TypeInfoDictionary infoDic = null; 42 42 43 43 #region 获取或创建类型的映射字典 44 44 lock (typeInfoDic) 45 45 { 46 46 if (typeInfoDic.ContainsKey(modelType)) 47 47 { 48 48 infoDic = typeInfoDic[modelType]; 49 49 } 50 50 #region 内存中无该类型的映射字典,则创建 51 51 if (infoDic == null) 52 52 { 53 53 infoDic = new TypeInfoDictionary(); 54 54 infoDic.typeCon = modelType.GetConstructor(Type.EmptyTypes); 55 55 Type outMappingType = typeof(Attributes.OutDataMappingAttribute);//这是映射列特性 属性 ColumnName string 56 56 Type defaultValueType = typeof(Attributes.DefaultValueAttribute);//这是默认值特性 属性 DefaultValue object 57 57 PropertyInfo[] pinfos = modelType.GetProperties(); 58 58 foreach (PropertyInfo p in pinfos) 59 59 { 60 60 #region 查找两个特性值 61 61 object[] out_attrs = p.GetCustomAttributes(outMappingType, true); 62 62 object[] default_attr = p.GetCustomAttributes(defaultValueType, true); 63 63 Attributes.OutDataMappingAttribute out_attr = null; 64 64 Attributes.DefaultValueAttribute defaultAttr = null; 65 65 if (out_attrs != null && out_attrs.Length > 0) 66 66 { 67 67 out_attr = out_attrs[out_attrs.Length - 1] as Attributes.OutDataMappingAttribute; 68 68 } 69 69 if (default_attr != null && default_attr.Length > 0) 70 70 { 71 71 defaultAttr = default_attr[default_attr.Length - 1] as Attributes.DefaultValueAttribute; 72 72 } 73 73 #endregion 74 74 75 75 if (out_attr != null) 76 76 { 77 77 TypeInfo typeInfo = new TypeInfo(); 78 78 if (defaultAttr!= null && 79 79 defaultAttr.DefaultValue != null) 80 80 { 81 81 typeInfo.DefaultValue =Convert.ChangeType(defaultAttr.DefaultValue,p.PropertyType); 82 82 } 83 83 typeInfo.PropertyInfo = p; 84 84 typeInfo.MappingColumnName = out_attr.ColumnName; 85 85 infoDic.Add(out_attr.ColumnName, typeInfo); 86 86 } 87 87 } 88 88 typeInfoDic.Add(modelType, infoDic); 89 89 } 90 90 #endregion 91 91 } 92 92 #endregion 93 93 94 94 #region 创建动态方法 95 95 Dictionary<string, int> columNameParamterDic = new Dictionary<string, int>(); 96 96 List<Type> typeList = new List<Type>(); 97 97 int i = 0; 98 98 foreach (string columnName in infoDic.Keys) 99 99 { 100 100 if (table.Columns.Contains(columnName)) 101 101 { 102 102 columNameParamterDic.Add(columnName, i); 103 103 TypeInfo typeInfo = infoDic[columnName]; 104 104 typeList.Add(typeInfo.PropertyInfo.PropertyType); 105 105 i++; 106 106 } 107 107 } 108 108 DynamicMethod dyMethod = new DynamicMethod(METHOD_NAME,modelType, typeList.ToArray(),modelType,true); 109 109 ILGenerator dyGL = dyMethod.GetILGenerator(); 110 110 LocalBuilder nModel = dyGL.DeclareLocal(modelType); 111 111 //这段IL相当于创建一个模型类实例,并将调用其中的属性 112 112 dyGL.Emit(OpCodes.Newobj, infoDic.typeCon); 113 113 dyGL.Emit(OpCodes.Stloc_0); 114 114 foreach (string columnName in columNameParamterDic.Keys) 115 115 { 116 116 TypeInfo tInfo = infoDic[columnName]; 117 117 dyGL.Emit(OpCodes.Ldloc_0); 118 118 dyGL.Emit(OpCodes.Ldarg, columNameParamterDic[columnName]); 119 119 dyGL.Emit(OpCodes.Callvirt, tInfo.PropertyInfo.GetSetMethod()); 120 120 } 121 121 dyGL.Emit(OpCodes.Ldloc_0); 122 122 dyGL.Emit(OpCodes.Ret); 123 123 #endregion 124 124 125 125 #region 调用动态方法创建模型类,并把数据放入实例中 126 126 foreach (DataRow row in table.Rows) 127 127 { 128 128 object[] objs = new object[columNameParamterDic.Count]; 129 129 foreach (string columnName in columNameParamterDic.Keys) 130 130 { 131 131 int index = columNameParamterDic[columnName];//相当于表明参数位置 132 132 object defaultValue = infoDic[columnName].DefaultValue; 133 133 if (!row.IsNull(columnName)) 134 134 { 135 135 objs[index] = row[columnName]; 136 136 } 137 137 else 138 138 { 139 139 if (defaultValue != null) 140 140 { 141 141 objs[index] = defaultValue; 142 142 } 143 143 } 144 144 } 145 145 T mData = dyMethod.Invoke(null, objs) as T; 146 146 list.Add(mData); 147 147 } 148 148 #endregion 149 149 return list; 150 150 } 151 151 } 152 152 class TypeInfo 153 153 { 154 154 private PropertyInfo propertyInfo; 155 155 156 156 public PropertyInfo PropertyInfo 157 157 { 158 158 get { return propertyInfo; } 159 159 set { propertyInfo = value; } 160 160 } 161 161 private string mappingColumnName; 162 162 163 163 public string MappingColumnName 164 164 { 165 165 get { return mappingColumnName; } 166 166 set { mappingColumnName = value; } 167 167 } 168 168 private object defaultValue; 169 169 170 170 public object DefaultValue 171 171 { 172 172 get { return defaultValue; } 173 173 set { defaultValue = value; } 174 174 } 175 175 } 176 176 177 177 class TypeInfoDictionary : Dictionary<string, TypeInfo> 178 178 { 179 179 internal ConstructorInfo typeCon; 180 180 } 181 181 }
浙公网安备 33010602011771号