C#如何在没有ORM的情况下从RDBMS填充对象列表

问题描述

我想从 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;
    }
}