You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用AutoMapper将System.Data.DataTable内容复制到List<T>时映射值为空的问题排查

问题分析与解决方案

先给你捋捋可能的问题所在,我之前做DataTable转实体列表的时候也踩过这些坑😉

核心问题原因

你的AutoMapper映射失败,大概率是以下几个原因之一:

  • 字段/属性名称不匹配:AutoMapper默认是大小写敏感的。比如DataTable里的列名是StartDate(首字母大写),但你的DataModel属性是startdate(全小写),这就会导致AutoMapper找不到对应字段,只能给属性赋默认值(DateTime默认是01-01-0001,string默认是null)。
  • AutoMapper未正确配置映射规则:对于IDataReader到实体的映射,尤其是旧版本的AutoMapper,可能需要显式定义映射关系,而不是完全依赖自动映射。
  • 使用了过时的AutoMapper API:你用的Mapper.DynamicMap是AutoMapper早期版本(比如4.x及以前)的API,这类动态映射对DataReader的支持并不完善,新版本已经废弃了这个方法,改用更稳定的Mapper.Map或者依赖注入方式。

具体解决方案

1. 先检查字段名称匹配

第一步先确认DataTable的列名和DataModel的属性名完全一致(包括大小写)。比如:

  • DataTable列名必须是startdateenddatesubject,和你的实体属性名完全对应;
  • 如果列名是其他格式(比如StartDate),就需要显式配置映射。

2. 正确配置AutoMapper映射

方式一:使用Profile定义映射规则

创建一个映射配置类,明确指定DataReader到DataModel的映射关系:

public class DataModelMappingProfile : Profile
{
    public DataModelMappingProfile()
    {
        CreateMap<IDataReader, DataModel>()
            // 如果列名和属性名一致,这三行可以省略;如果不一致,就手动指定对应关系
            .ForMember(dest => dest.startdate, opt => opt.MapFrom(reader => reader["startdate"]))
            .ForMember(dest => dest.enddate, opt => opt.MapFrom(reader => reader["enddate"]))
            .ForMember(dest => dest.subject, opt => opt.MapFrom(reader => reader["subject"]));
    }
}

然后在应用启动时初始化AutoMapper:

// 旧版本AutoMapper(4.x-7.x)
Mapper.Initialize(cfg => cfg.AddProfile<DataModelMappingProfile>());

// 新版本AutoMapper(8.x+,推荐)
var mapperConfig = new MapperConfiguration(cfg => 
{
    cfg.AddProfile<DataModelMappingProfile>();
});
// 可以把mapper注入到依赖容器,或者直接实例化使用
var mapper = mapperConfig.CreateMapper();

最后修改你的ReadData方法:

// 旧版本用法
List<T> ReadData<T>(DataTable dt) 
{ 
    return Mapper.Map<IDataReader, List<T>>(dt.CreateDataReader()); 
}

// 新版本用法(需要传入IMapper实例)
List<T> ReadData<T>(DataTable dt, IMapper mapper) 
{ 
    return mapper.Map<IDataReader, List<T>>(dt.CreateDataReader()); 
}

3. 备选方案:手动循环映射(不依赖AutoMapper)

如果你暂时不想折腾AutoMapper的配置,也可以用手动循环的方式实现,这种方式更直观,也不容易出问题:

List<DataModel> ReadData(DataTable dt)
{
    var dataList = new List<DataModel>();
    foreach (DataRow row in dt.Rows)
    {
        var model = new DataModel
        {
            // 注意处理DBNull.Value的情况,避免转换报错
            startdate = row["startdate"] != DBNull.Value ? Convert.ToDateTime(row["startdate"]) : DateTime.MinValue,
            enddate = row["enddate"] != DBNull.Value ? Convert.ToDateTime(row["enddate"]) : DateTime.MinValue,
            subject = row["subject"] != DBNull.Value ? row["subject"].ToString() : null
        };
        dataList.Add(model);
    }
    return dataList;
}

额外提醒

  • 如果你用的是非常旧的AutoMapper版本,建议升级到最新稳定版,新版本对DataReader的映射支持更好,API也更规范;
  • 手动映射时一定要处理DBNull.Value,否则当DataTable里有null值时会抛出转换异常。

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

火山引擎 最新活动