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

C#中DataTable转自定义实体类的高效赋值方法咨询

优化订单实体映射与按需计算的实现方案

一、替换手动字段映射的简洁方案

1. 手写轻量级反射映射(无需第三方库)

针对列名和实体字段/属性名一致的场景,自己写个通用映射方法,一次性搞定40多个字段的赋值,不用逐行写:

public static T MapDataRowToEntity<T>(DataRow row) where T : new()
{
    var entity = new T();
    var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);

    foreach (var field in fields)
    {
        if (row.Table.Columns.Contains(field.Name) && row[field.Name] != DBNull.Value)
        {
            try
            {
                var value = Convert.ChangeType(row[field.Name], field.FieldType);
                field.SetValue(entity, value);
            }
            catch (Exception ex)
            {
                // 这里可以加日志记录,比如记录字段名和转换异常信息
            }
        }
    }
    return entity;
}

用的时候一行代码搞定:

OrderDtl order = MapDataRowToEntity<OrderDtl>(oDT.Rows[0]);

2. 用AutoMapper做成熟映射(适合已有该库的项目)

如果项目里已经在用AutoMapper,配置好映射规则后,代码更简洁,还能灵活处理特殊字段:

先配置映射:

var mapperConfig = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<DataRow, OrderDtl>()
       .ForMember(dest => dest.OrderLineCount, opt => opt.Ignore()); // 忽略这个字段,留着自己按需计算
});
var mapper = mapperConfig.CreateMapper();

映射实体:

OrderDtl order = mapper.Map<OrderDtl>(oDT.Rows[0]);

二、按需计算字段的实现方式

把像OrderLineCount这类不需要从数据库预加载的字段,改成只读属性,只在访问时计算:

1. 基础按需计算(适合简单逻辑)

直接修改实体类,把字段换成属性,访问时实时计算:

public class OrderDtl
{
    public int OrderNum;
    public string Name;
    public string ShipToAddress1;         
    public string ShipToAddress2;
    public string ShipToCity;
    public string ShipToState;
    // 新增订单行集合,后续从其他渠道加载(比如单独查库)
    public List<OrderLine> OrderLines { get; set; } = new List<OrderLine>();

    // 访问时才计算订单行数
    public int OrderLineCount => OrderLines?.Count ?? 0;

    // 其他类似按需计算的字段同理
    public decimal TotalOrderAmount => OrderLines?.Sum(line => line.LineAmount) ?? 0;
}

2. 缓存计算结果(适合复杂计算逻辑)

如果计算逻辑比较耗时(比如涉及多步运算),可以加个私有字段缓存结果,避免重复计算:

public class OrderDtl
{
    // ... 其他字段省略
    public List<OrderLine> OrderLines { get; set; } = new List<OrderLine>();
    
    private int? _cachedLineCount;
    public int OrderLineCount
    {
        get
        {
            if (_cachedLineCount == null)
            {
                // 这里放复杂计算逻辑,比如过滤特定状态的订单行再统计
                _cachedLineCount = OrderLines?.Count(line => line.IsActive) ?? 0;
            }
            return _cachedLineCount.Value;
        }
    }

    // 如果订单行集合更新了,要重置缓存
    public void RefreshOrderLines(List<OrderLine> newLines)
    {
        OrderLines = newLines;
        _cachedLineCount = null; // 清空缓存,下次访问会重新计算
    }
}

三、额外性能优化建议

  • 反射映射性能优化:如果这个映射是高频调用,普通反射可能有开销,可以用表达式树提前编译映射逻辑,或者用System.Reflection.Emit生成动态方法,比普通反射快不少。
  • 数据库层面优化:移除存储过程里的预计算字段后,确保SP只返回需要的列,减少数据传输量。
  • 异步懒加载:如果订单行需要单独查库,可以做成异步懒加载,避免一开始就加载所有数据:
public class OrderDtl
{
    private readonly Lazy<Task<List<OrderLine>>> _lazyOrderLines;
    
    public OrderDtl(int orderNum)
    {
        OrderNum = orderNum;
        _lazyOrderLines = new Lazy<Task<List<OrderLine>>>(() => LoadOrderLinesFromDbAsync(orderNum));
    }

    public async Task<int> GetOrderLineCountAsync()
    {
        var lines = await _lazyOrderLines.Value;
        return lines?.Count ?? 0;
    }

    private async Task<List<OrderLine>> LoadOrderLinesFromDbAsync(int orderNum)
    {
        // 这里写查询订单行的异步数据库逻辑
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 09:25:32