C#中DataTable 转实体实例详解
程序员文章站
2023-08-15 20:33:04
因为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 转实体实例详解,希望对大家有所帮助