使用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列名必须是
startdate、enddate、subject,和你的实体属性名完全对应; - 如果列名是其他格式(比如
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




