C#反射实现嵌套对象填充:DataTable递归赋值需求
递归反射实现DataTable到嵌套对象集合的转换
核心思路
通过递归反射遍历目标类型的所有属性,根据属性类型区分处理:
- 对于值类型、字符串或可空类型,直接从DataTable行中匹配同名列赋值
- 对于引用类型(非字符串),检查DataTable中是否存在以
父属性名_为前缀的列,若存在则递归创建并填充嵌套对象,无需额外数据库查询
完整实现代码
using System; using System.Collections.Generic; using System.Data; using System.Reflection; public static class ReflectConverter { /// <summary> /// 将DataTable转换为指定类型的对象集合 /// </summary> /// <typeparam name="T">目标对象类型</typeparam> /// <param name="dt">数据源DataTable</param> /// <returns>对象集合</returns> public static List<T> Reflect<T>(DataTable dt) where T : new() { var resultList = new List<T>(); foreach (DataRow row in dt.Rows) { resultList.Add(CreateAndFillNestedObject<T>(row)); } return resultList; } /// <summary> /// 递归创建并填充对象(包括嵌套子对象) /// </summary> private static T CreateAndFillNestedObject<T>(DataRow row) where T : new() { var targetObj = new T(); var targetType = typeof(T); foreach (PropertyInfo prop in targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var propType = prop.PropertyType; var nestedColumnPrefix = $"{prop.Name}_"; // 处理值类型、字符串、可空值类型 if (IsSimpleType(propType)) { if (row.Table.Columns.Contains(prop.Name)) { var columnValue = row[prop.Name]; if (columnValue != DBNull.Value) { prop.SetValue(targetObj, Convert.ChangeType(columnValue, GetUnderlyingType(propType))); } } } // 处理引用类型(递归创建子对象) else if (propType.IsClass && propType != typeof(string)) { // 检查是否存在该嵌套对象对应的列(如Bar_Id、Bar_SubBar_Detail) bool hasNestedColumns = row.Table.Columns.Cast<DataColumn>() .Any(col => col.ColumnName.StartsWith(nestedColumnPrefix)); if (hasNestedColumns) { // 动态调用泛型方法创建子对象 var createMethod = typeof(ReflectConverter) .GetMethod(nameof(CreateAndFillNestedObject), BindingFlags.NonPublic | BindingFlags.Static) ?.MakeGenericMethod(propType); if (createMethod != null) { var nestedObj = createMethod.Invoke(null, new object[] { row }); prop.SetValue(targetObj, nestedObj); } } } } return targetObj; } /// <summary> /// 判断是否为简单类型(值类型、字符串、可空值类型) /// </summary> private static bool IsSimpleType(Type type) { return type.IsValueType || type == typeof(string) || Nullable.GetUnderlyingType(type) != null; } /// <summary> /// 获取可空类型的基础类型,非空类型则返回原类型 /// </summary> private static Type GetUnderlyingType(Type type) { return Nullable.GetUnderlyingType(type) ?? type; } }
关键细节说明
- 列名约定:嵌套对象的属性需对应DataTable中
父属性名_子属性名格式的列,例如Bar_SubBar_Detail对应Foo.Bar.SubBar.Detail,这样可以直接从同一张表提取嵌套数据,无需额外查询 - 递归扩展性:无论后续给
Bar、Cas新增多少层子对象,只要列名遵循约定,递归逻辑会自动处理,无需修改转换方法 - 类型安全处理:封装了
IsSimpleType和GetUnderlyingType方法,统一处理值类型、可空类型和引用类型的赋值逻辑,避免类型转换异常 - 空值处理:判断列值是否为
DBNull.Value,避免将数据库空值赋值给对象属性导致的错误
使用示例
实体类定义
public class Foo { public int Id { get; set; } public string Name { get; set; } public Bar Bar { get; set; } public Cas Cas { get; set; } } public class Bar { public int Id { get; set; } public string Title { get; set; } // 后续新增的子对象 public SubBar SubBar { get; set; } } public class Cas { public string Code { get; set; } } public class SubBar { public string Detail { get; set; } }
调用方式
// 假设从数据库获取包含以下列的DataTable: // Id, Name, Bar_Id, Bar_Title, Bar_SubBar_Detail, Cas_Code DataTable dataTable = FetchDataFromDatabase(); List<Foo> fooCollection = ReflectConverter.Reflect<Foo>(dataTable);
内容的提问来源于stack exchange,提问作者DrJaul
相关产品推荐
相关产品推荐

