再谈使用Emit把Datatable转换为对象集合(List<T>)
一、前因和存在的问题前面我写了一篇《使用Emit把Datatable转换为对象集合(List<T>)》的博文,其实起源于我自己编写的一个orm工具(见前面几篇博文有介绍),里面已有用emit把datareader转换为List<T>的实现方法,但是需要增加一个把DataTable转换为List<T>的方法,在网上搜索了一些代码,经过改造,加入缓存设计,整理了一下代码结构,简单测试没有问题后就发了《使用Emit把Datatable转换为对象集合(List<T>)》一文,但是不久以后我拿这些代码和我以前写的对datareader的转换的代码比较,发现差异较大,于是仔细对比研究了一下,发现datatable的转换方法存在一个不足。即待转换的datatable的架构被严格限制,不够灵活。二、分析产生原因我们看一下利用emit动态创建一个 把datarow转换为一个实体对象的方法的核心部分代码1 for (int index = 0; index < dt.Columns.Count; index++)2 {3 PropertyInfo propertyInfo = typeof(T).GetProperty(dt.Columns[index].ColumnName,StringComparison.CurrentCultureIgnoreCase);4 Label endIfLabel = generator.DefineLabel();5 if (propertyInfo != null && propertyInfo.GetSetMethod() != null)6 {7 generator.Emit(OpCodes.Ldarg_0);8 generator.Emit(OpCodes.Ldc_I4, index);9 generator.Emit(OpCodes.Callvirt, isDBNullMethod);10 generator.Emit(OpCodes.Brtrue, endIfLabel);11 generator.Emit(OpCodes.Ldloc, result);12 generator.Emit(OpCodes.Ldarg_0);13 generator.Emit(OpCodes.Ldc_I4, index);14 generator.Emit(OpCodes.Callvirt, getValueMethod);15 generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);16 generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());17 generator.MarkLabel(endIfLabel);18 }19 }emit的语法比较奥涩难懂,我们不追究细节,假设datatable的列集合为{"a","b","c"},实体对象E的属性有{a,b,c},粗略模拟生成的代码大致为public E Convert(DataRow dr){E e = new E();....if(dr[0]!=DBNull.Value)e.a=dr[0];if(dr[1]!=DBNull.Value)e.b=dr[1];if(dr[2]!=DBNull.Value)e.c=dr[2];return e;}这里有什么问题呢?就是所生成的代码,是先遍历datatable的列,然后检查实体E中是否含有匹配的属性,如果把此方法缓存起来,下一次调用时,碰巧datatable的架构有所变化,如列数只有两列,执行到 if(dr[2]!=DBNull.Value), 就会出现“索引超出范围”了。所以我认为应该先遍历E的属性,然后检查datatable是否含匹配的列。动态生成的代码应该大致如下public E Convert(DataRow dr){E e = new E();if dr.Table.Columns.Contains("a") && !dr.IsNull("a")e.a = dr["a"];if dr.Table.Columns.Contains("b") && !dr.IsNull("b")e.b = dr["b"];if dr.Table.Columns.Contains("c") && !dr.IsNull("c")e.c = dr["c"];return e;}上述代码,不管datatable如何变化,都只会转换匹配的列而不会出错。三、解决的办法和成果所以,我后来还是把我以前的代码加以改造实现了datatable的转换,并把datareader和datatable两个转换方法合并到一个类下面。代码如下using System;using System.Collections.Generic;using System.Text;using System.Data;using System.Reflection;using System.Reflection.Emit;using System.Web.Caching;using System.Web;namespace LinFramework{/// <summary>/// 实体转换/// </summary>public class EntityConverter{//数据类型和对应的强制转换方法的methodinfo,供实体属性赋值时调用private static Dictionary<Type, MethodInfo> ConvertMethods = new Dictionary<Type, MethodInfo>(){{typeof(int),typeof(Convert).GetMethod("ToInt32",new Type[]{typeof(object)})},{typeof(Int16),typeof(Convert).GetMethod("ToInt16",new Type[]{typeof(object)})},{typeof(Int64),typeof(Convert).GetMethod("ToInt64",new Type[]{typeof(object)})},{typeof(DateTime),typeof(Convert).GetMethod("ToDateTime",new Type[]{typeof(object)})},{typeof(decimal),typeof(Convert).GetMethod("ToDecimal",new Type[]{typeof(object)})},{typeof(Double),typeof(Convert).GetMethod("ToDouble",new Type[]{typeof(object)})},{typeof(Boolean),typeof(Convert).GetMethod("ToBoolean",new Type[]{typeof(object)})},{typeof(string),typeof(Convert).GetMethod("ToString",new Type[]{typeof(object)})}&nb补充:综合编程 , 其他综合 ,
上一个:多线程控制
下一个:CF算法计算商品间相似度