问题描述
我想从 DataTable 填充对象列表,而不是逐列显式分配数据元素,也不使用 ORM。使用 Dapper(一个小型 ORM),您可以传递一个 POCO(普通对象)列表,并让它根据该 POCO 的“形状”自动匹配查询中的“单元格”。我想使用 DataTable 对象做类似的事情。有没有办法在不使用 Dapper 的情况下镜像该功能?我想可以使用反射,但是让反射可靠地工作通常需要“火箭科学”,尤其是在调试方面。性能是一个相对较小的问题。
public class Employee
{
public string lastName { get; set; }
public string firstAndMidName { get; set; }
public int employeeNumber { get; set; }
public int salary { get; set; }
}
// ...
public void runSample()
{
List<Employee> employeeList = new List<Employee>();
DataTable myDat = queryRDBMS("select * from Employees"); // typical query API (simplified)
employeeList = convertDataTabletoList(myDat,modelClass: Employee); // the missing part
}
(更新)
解决方法
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
public static class DataTableExtensions
{
public static List<T> MapToList<T>(this DataTable dt)
{
string currentPropName = string.Empty;
try
{
List<T> list = new List<T>();
T obj = default(T);
if (dt?.Rows?.Count > 0)
{
foreach (DataRow row in dt.Rows)
{
obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties())
{
// Check has column in case we have extension property
if (!dt.HasColumn(prop.Name)) continue;
currentPropName = prop.Name;
if (!object.Equals(row[prop.Name],DBNull.Value))
{
// Need to check object type boolean here because return result from db type will be ulong64 which can't convert to bool
if (IsBoolean(prop))
{
prop.SetValue(obj,Convert.ToBoolean(row[prop.Name]),null);
}
else
{
prop.SetValue(obj,row[prop.Name],null);
}
}
}
list.Add(obj);
}
}
return list;
}
catch (Exception ex)
{
throw new Exception($"Error occured while converting object '{currentPropName}' from data table to obj class ",ex);
}
}
private static bool IsBoolean(PropertyInfo prop)
{
return prop.PropertyType == typeof(bool) || prop.PropertyType == typeof(bool?);
}
public static bool HasColumn(this DataTable dt,string columnName)
{
for (int i = 0; i < dt.Columns.Count; i++)
{
if (dt.Columns[i].ColumnName.Equals(columnName,StringComparison.InvariantCultureIgnoreCase)) return true;
}
return false;
}
}