You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过反射实现SqlDataReader到嵌套自定义类的映射

问题核心原因

你现有代码的核心错误是 accessorNested[mem.Type, fieldName] = rd.GetValue(i); 这行写法不符合TypeAccessor的使用规则:TypeAccessor索引器的第一个参数需要传入具体的实例对象,你传入类型本身只会尝试给静态成员赋值,完全不会把值写入你创建的主对象t的嵌套属性实例中。
此外现有逻辑还缺少嵌套实例为空的初始化逻辑,也不支持多层递归嵌套。

修复后实现

下面的实现支持单层/多层自定义类型属性映射,保留原有的大小写不敏感匹配规则:

using FastMember;
using System.Data.SqlClient;

internal static async Task<IEnumerable<T>> ConvertToNestedObjectsAsync<T>(this SqlDataReader rd) where T : class, new()
{
    Type type = typeof(T);
    var accessor = TypeAccessor.Create(type);
    var members = accessor.GetMembers();
    var tList = new List<T>();

    while (await rd.ReadAsync())
    {
        var t = new T();
        for (int i = 0; i < rd.FieldCount; i++)
        {
            if (rd.IsDBNull(i)) continue;
            
            string fieldName = rd.GetName(i);
            object fieldValue = rd.GetValue(i);
            
            // 先匹配主类的直接成员
            if (members.Any(m => string.Equals(m.Name, fieldName, StringComparison.OrdinalIgnoreCase)))
            {
                accessor[t, fieldName] = fieldValue;
                continue;
            }
            
            // 匹配嵌套类成员
            SetNestedPropertyValue(t, fieldName, fieldValue);
        }
        tList.Add(t);
    }

    return tList;
}

// 递归赋值嵌套属性的辅助方法
private static void SetNestedPropertyValue(object instance, string fieldName, object value)
{
    var instanceType = instance.GetType();
    var accessor = TypeAccessor.Create(instanceType);
    var nonPrimitiveMembers = accessor.GetMembers().Where(m => !IsPrimitive(m.Type)).ToList();

    foreach (var mem in nonPrimitiveMembers)
    {
        var nestedAccessor = TypeAccessor.Create(mem.Type);
        var nestedMembers = nestedAccessor.GetMembers();
        
        // 匹配嵌套类的字段名
        if (nestedMembers.Any(m => string.Equals(m.Name, fieldName, StringComparison.OrdinalIgnoreCase)))
        {
            // 嵌套实例为空则先初始化
            var nestedInstance = accessor[instance, mem.Name];
            if (nestedInstance == null)
            {
                // 要求嵌套类型必须有无参构造函数
                nestedInstance = Activator.CreateInstance(mem.Type);
                accessor[instance, mem.Name] = nestedInstance;
            }
            // 给嵌套实例的对应属性赋值
            nestedAccessor[nestedInstance, fieldName] = value;
            return;
        }
        
        // 支持更深层级的递归匹配,比如A.B.C
        var deeperNestedInstance = accessor[instance, mem.Name];
        if (deeperNestedInstance == null)
        {
            deeperNestedInstance = Activator.CreateInstance(mem.Type);
            accessor[instance, mem.Name] = deeperNestedInstance;
        }
        SetNestedPropertyValue(deeperNestedInstance, fieldName, value);
    }
}

internal static bool IsPrimitive(Type t)
{
    return new[]
    {
        typeof(string),
        typeof(char),
        typeof(byte),
        typeof(sbyte),
        typeof(ushort),
        typeof(short),
        typeof(uint),
        typeof(int),
        typeof(ulong),
        typeof(long),
        typeof(float),
        typeof(double),
        typeof(decimal),
        typeof(DateTime),
        typeof(Guid),
        typeof(DateTimeOffset),
        typeof(bool)
    }.Contains(t) || t.IsPrimitive || t.IsEnum;
}

额外优化建议

  • 若存在多个嵌套类有同名字段的情况,建议给查询字段加前缀区分,比如Address_City对应Person.Address.City,匹配时按下划线拆分层级,避免冲突,也能提升匹配效率。
  • 如果你需要映射List<Fields>这类集合属性,需要基于主表的唯一键对查询结果分组:同一分组的主表字段只赋值一次,明细字段生成对应实例添加到集合中即可。

内容的提问来源于stack exchange,提问作者Justin Farrugia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 07:24:05