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
相关产品推荐
相关产品推荐

