欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C#中DataTable 转实体实例详解

程序员文章站 2023-11-30 10:38:16
因为linq的查询功能很强大,所以从数据库中拿到的数据为了处理方便,我都会转换成实体集合list。 开始用的是硬编码的方式,好理解,但通用性极低,下面是...

因为linq的查询功能很强大,所以从数据库中拿到的数据为了处理方便,我都会转换成实体集合list<t>。

开始用的是硬编码的方式,好理解,但通用性极低,下面是控件台中的代码:

using system;
using system.collections.generic;
using system.data;
using system.linq;
using system.text;
using system.threading.tasks;
namespace demo1
{
  class program
  {
    static void main(string[] args)
    {
      datatable dt = query();
      list<usr> usrs = new list<usr>(dt.rows.count);
      //硬编码,效率比较高,但灵活性不够,如果实体改变了,都需要修改代码
      foreach (datarow dr in dt.rows)
      {
        usr usr = new usr { id = dr.field<int32?>("id"), name = dr.field<string>("name") };
        usrs.add(usr);
      }
      usrs.clear();
    }
    /// <summary>
    /// 查询数据
    /// </summary>
    /// <returns></returns>
    private static datatable query()
    {
      datatable dt = new datatable();
      dt.columns.add("id", typeof(int32));
      dt.columns.add("name", typeof(string));
      for (int i = 0; i < 1000000; i++)
      {
        dt.rows.add(new object[] { i, guid.newguid().tostring() });
      }
      return dt;
    }
  }
  class usr
  {
    public int32? id { get; set; }
    public string name { get; set; }
  }
}

后来用反射来做这,对实体的属性用反射去赋值,这样就可以对所有的实体通用,且增加属性后不用修改代码。

程序如下:

static class entityconvert
  {
    /// <summary>
    /// datatable转为list<t>
    /// </summary>
    /// <typeparam name="t"></typeparam>
    /// <param name="dt"></param>
    /// <returns></returns>
    public static list<t> tolist<t>(this datatable dt) where t : class, new()
    {
      list<t> colletion = new list<t>();
      propertyinfo[] pinfos = typeof(t).getproperties();
      foreach (datarow dr in dt.rows)
      {
        t t = new t();
        foreach (propertyinfo pinfo in pinfos)
        {
          if (!pinfo.canwrite) continue;
          pinfo.setvalue(t, dr[pinfo.name]);
        }
        colletion.add(t);
      }
      return colletion;
    }
  }

增加一个扩展方法,程序更加通用。但效率不怎么样,100万行数据【只有两列】,转换需要2秒

后来想到用委托去做 委托原型如下

func<datarow, usr> func = dr => new usr { id = dr.field<int32?>("id"), name = dr.field<string>("name") };

代码如下:

static void main(string[] args)
    {
      datatable dt = query();
      func<datarow, usr> func = dr => new usr { id = dr.field<int32?>("id"), name = dr.field<string>("name") };
      list<usr> usrs = new list<usr>(dt.rows.count);
      stopwatch sw = stopwatch.startnew();
      foreach (datarow dr in dt.rows)
      {
        usr usr = func(dr);
        usrs.add(usr);
      }
      sw.stop();
      console.writeline(sw.elapsedmilliseconds);
      usrs.clear();
      console.readkey();
    }

速度确实快了很多,我电脑测试了一下,需要 0.4秒。但问题又来了,这个只能用于usr这个类,得想办法把这个类抽象成泛型t,既有委托的高效,又有泛型的通用。

问题就在动态地产生上面的委托了,经过一下午的折腾终于折腾出来了动态产生委托的方法。主要用到了动态lambda表达式

public static class entityconverter
  {
    /// <summary>
    /// datatable生成实体
    /// </summary>
    /// <typeparam name="t"></typeparam>
    /// <param name="datatable"></param>
    /// <returns></returns>
    public static list<t> tolist<t>(this datatable datatable) where t : class, new()
    {
      if (datatable == null || datatable.rows.count <= 0) throw new argumentnullexception("datatable", "当前对象为null无法生成表达式树");
      func<datarow, t> func = datatable.rows[0].toexpression<t>();
      list<t> collection = new list<t>(datatable.rows.count);
      foreach (datarow dr in datatable.rows)
      {
        collection.add(func(dr));
      }
      return collection;
    }
    /// <summary>
    /// 生成表达式
    /// </summary>
    /// <typeparam name="t"></typeparam>
    /// <param name="datarow"></param>
    /// <returns></returns>
    public static func<datarow, t> toexpression<t>(this datarow datarow) where t : class, new()
    {
      if (datarow == null) throw new argumentnullexception("datarow", "当前对象为null 无法转换成实体");
      parameterexpression paramter = expression.parameter(typeof(datarow), "dr");
      list<memberbinding> binds = new list<memberbinding>();
      for (int i = 0; i < datarow.itemarray.length; i++)
      {
        string colname = datarow.table.columns[i].columnname;
        propertyinfo pinfo = typeof(t).getproperty(colname);
        if (pinfo == null) continue;
        methodinfo minfo = typeof(datarowextensions).getmethod("field", new type[] { typeof(datarow), typeof(string) }).makegenericmethod(pinfo.propertytype);
        methodcallexpression call = expression.call(minfo, paramter, expression.constant(colname, typeof(string)));
        memberassignment bind = expression.bind(pinfo, call);
        binds.add(bind);
      }
      memberinitexpression init = expression.memberinit(expression.new(typeof(t)), binds.toarray());
      return expression.lambda<func<datarow, t>>(init, paramter).compile();
    }
  }

 经过测试,用这个方法在同样的条件下转换实体需要 0.47秒。除了第一次用反射生成lambda表达式外,后续的转换直接用的表达式。

以上所述是小编给大家介绍的c#中datatable 转实体实例详解,希望对大家有所帮助