Automapper映射TableEntity字典报错:需解析为顶级成员的解决方法
问题:Automapper映射字典类型TableEntity与POCO时的索引器错误
出现的错误:
Expression 'dest => dest.get_Item("DeviceName")' must resolve to top-level member and not any child object's properties. You can use ForPath, a custom resolver on the child type or the AfterMap option instead. (Parameter 'lambdaExpression')
DTO定义
public class Enrollment { public Guid Id { get; set; } public string DeviceName { get; set; } public DateTimeOffset InitiatedOn { get; set; } public AttestationStatus Status { get; set; } = AttestationStatus.Pending; public string? KeyReleasedToAddress { get; set; } } public enum AttestationStatus { Pending = 0, Approved = 1, Rejected = 2 }
TableEntity类(索引器部分)
public partial class TableEntity : IDictionary<string, object> { /// <summary>Gets or sets the entity's property, given the name of the property.</summary> /// <param name="key">A string containing the name of the property.</param> /// <returns>The property value typed as an object.</returns> public object this[string key] { get { return GetValue(key); } set { SetValue(key, value); } } /* snip */ }
原映射配置
private static readonly IMapper Mapper = new MapperConfiguration(cfg => { cfg.CreateMap<Enrollment, TableEntity>() .ForMember(dest => dest.RowKey, opt => opt.MapFrom(src => src.Id.ToString())) .ForMember(dest => dest["DeviceName"], opt => opt.MapFrom(src => src.DeviceName)) .ForMember(dest => dest["InitiatedOn"], opt => opt.MapFrom(src => src.InitiatedOn)) .ForMember(dest => dest["Status"], opt => opt.MapFrom(src => src.Status.ToString())) .ForMember(dest => dest["KeyReleasedToAddress"], opt => opt.MapFrom(src => src.KeyReleasedToAddress)) .ReverseMap() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => Guid.Parse(src.RowKey))) .ForMember(dest => dest.Status, opt => opt.MapFrom(src => Enum.Parse<AttestationStatus>(src.GetString("Status")))); }) .CreateMapper();
触发异常的代码
public async Task<Enrollment?> Get(Guid tenant, Guid enrollment, CancellationToken cancellationToken = default) { var entry = await tableClient.GetEntityAsync<TableEntity>( tenant.ToString(), enrollment.ToString(), cancellationToken: cancellationToken); return entry == null ? null : Mapper.Map<TableEntity, Enrollment>(entry); }
解决方案
错误核心是Automapper的ForMember无法直接处理索引器这类非顶级成员的访问,需改用以下方式解决:
方法1:使用ForPath替代ForMember处理索引器映射
ForPath专门用于嵌套路径或索引器的映射场景,替换原配置中使用索引器的ForMember即可:
private static readonly IMapper Mapper = new MapperConfiguration(cfg => { cfg.CreateMap<Enrollment, TableEntity>() .ForMember(dest => dest.RowKey, opt => opt.MapFrom(src => src.Id.ToString())) // 用ForPath替代ForMember处理索引器赋值 .ForPath(dest => dest["DeviceName"], opt => opt.MapFrom(src => src.DeviceName)) .ForPath(dest => dest["InitiatedOn"], opt => opt.MapFrom(src => src.InitiatedOn)) .ForPath(dest => dest["Status"], opt => opt.MapFrom(src => src.Status.ToString())) .ForPath(dest => dest["KeyReleasedToAddress"], opt => opt.MapFrom(src => src.KeyReleasedToAddress)) .ReverseMap() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => Guid.Parse(src.RowKey))) .ForMember(dest => dest.Status, opt => opt.MapFrom(src => Enum.Parse<AttestationStatus>(src.GetString("Status")))); }) .CreateMapper();
方法2:使用AfterMap手动赋值(备选)
如果ForPath不符合需求,可在映射完成后手动通过索引器赋值:
cfg.CreateMap<Enrollment, TableEntity>() .ForMember(dest => dest.RowKey, opt => opt.MapFrom(src => src.Id.ToString())) .AfterMap((src, dest) => { dest["DeviceName"] = src.DeviceName; dest["InitiatedOn"] = src.InitiatedOn; dest["Status"] = src.Status.ToString(); dest["KeyReleasedToAddress"] = src.KeyReleasedToAddress; }) .ReverseMap() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => Guid.Parse(src.RowKey))) .ForMember(dest => dest.Status, opt => opt.MapFrom(src => Enum.Parse<AttestationStatus>(src.GetString("Status"))));
方法3:自定义类型转换器(复杂场景适用)
若需更灵活的映射逻辑,可创建自定义转换器:
public class EnrollmentToTableEntityConverter : ITypeConverter<Enrollment, TableEntity> { public TableEntity Convert(Enrollment source, TableEntity destination, ResolutionContext context) { destination ??= new TableEntity(); destination.RowKey = source.Id.ToString(); destination["DeviceName"] = source.DeviceName; destination["InitiatedOn"] = source.InitiatedOn; destination["Status"] = source.Status.ToString(); destination["KeyReleasedToAddress"] = source.KeyReleasedToAddress; return destination; } } // 配置映射时绑定转换器 cfg.CreateMap<Enrollment, TableEntity>().ConvertUsing<EnrollmentToTableEntityConverter>();
注意:反向映射(TableEntity到Enrollment)的配置无需修改,其未使用索引器作为目标成员,逻辑已正确。
内容的提问来源于stack exchange,提问作者morleyc
相关产品推荐
相关产品推荐

