首页 > 技术文章 > 自用的基于Emit的C#下DataTable转实体类方法

zyh1989 2017-01-05 19:02 原文

之前一直在做WebForm的开发,数据绑定时直接DataTable绑定Gridview很方便,但是最近开始往MVC转,数据列表的传递和页面展示基本上是以List为主,像下面这样,遍历实体类的各个字段去赋值的办法当然是最浪费时间的。

			if (row["ID"] != null && row["ID"].ToString() != "")
            {
                model.bedID = int.Parse(row["ID"].ToString());
            }

通过在网上查资料和一定的比较,我选择了以下基于Emit的DataTable转实体类的方法。
按照下边的代码新建一个类EntityConverter:

  public static class EntityConverter
  {
        public static List<T> ToList<T>(this DataTable dt) where T : class, new()
        {
            List<T> list = new List<T>();
            if (dt == null || dt.Rows.Count == 0)
                return list;
            DataTableEntityBuilder<T> eblist = DataTableEntityBuilder<T>.CreateBuilder(dt.Rows[0]);
            foreach (DataRow info in dt.Rows)
                list.Add(eblist.Build(info));
            dt.Dispose();
            dt = null;
            return list;
        }
        public class DataTableEntityBuilder<Entity>
        {
            private static readonly MethodInfo getValueMethod = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(int) });
            private static readonly MethodInfo isDBNullMethod = typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(int) });
            private delegate Entity Load(DataRow dataRecord);
            private Load handler;
            private DataTableEntityBuilder() { }
            public Entity Build(DataRow dataRecord)
            {
                return handler(dataRecord);
            }
            public static DataTableEntityBuilder<Entity> CreateBuilder(DataRow dataRecord)
            {
                DataTableEntityBuilder<Entity> dynamicBuilder = new DataTableEntityBuilder<Entity>();
                DynamicMethod method = new DynamicMethod("DynamicCreateEntity", typeof(Entity), new Type[] { typeof(DataRow) }, typeof(Entity), true);
                ILGenerator generator = method.GetILGenerator();
                LocalBuilder result = generator.DeclareLocal(typeof(Entity));
                generator.Emit(OpCodes.Newobj, typeof(Entity).GetConstructor(Type.EmptyTypes));
                generator.Emit(OpCodes.Stloc, result);
                for (int i = 0; i < dataRecord.ItemArray.Length; i++)
                {
                    PropertyInfo propertyInfo = typeof(Entity).GetProperty(dataRecord.Table.Columns[i].ColumnName);
                    Label endIfLabel = generator.DefineLabel();
                    if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
                    {
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Ldc_I4, i);
                        generator.Emit(OpCodes.Callvirt, isDBNullMethod);
                        generator.Emit(OpCodes.Brtrue, endIfLabel);
                        generator.Emit(OpCodes.Ldloc, result);
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Ldc_I4, i);
                        generator.Emit(OpCodes.Callvirt, getValueMethod);
                        generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                        generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                        generator.MarkLabel(endIfLabel);
                    }
                }
                generator.Emit(OpCodes.Ldloc, result);
                generator.Emit(OpCodes.Ret);
                dynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load));
                return dynamicBuilder;
            }
        }
  }

在DbHelperSQL里面再简单封装一下:

	  public static List<T> Query<T>(string sql) where T : class, new()
        {
            DataTable dt = DbHelperSQL.Query(sql).Tables[0];
            if (dt != null && dt.Rows.Count > 0)
            {
                return EntityConverter.ToList<T>(dt);
            }
            else
            {
                return null;
            } 
        }

使用的时候:

	string sql="select top 10 * from user";
	List<Model.User> listUser=DbHelperSQL.Query<Model.User>(sql);

注意:

  • 1、定义的Model的各个属性的名称要和Select语句执行结果的列名一致
  • 2、定义的Model的各个属性的数据类型要和数据库定义的一致
  • 3、关于数据列与属性对应:Model中的属性SQL里面可以没有,值会默认为Null/0;但是SQL结果里面有的列名必须在Model中存在。否则会报错!

这只是简单的使用,更深层次的需求请参考再谈使用Emit把Datatable转换为对象集合(List) - lindping

原文链接 http://huisky.com/blog/17010514150692

参考资料

推荐阅读