当前位置:编程学习 > 网站相关 >>

再谈使用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
补充:综合编程 , 其他综合 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,