如何使用AutoMapper的ProjectTo映射包含子实体的对象?
问题描述
实体类定义
public class Customer { public long Id { get; set; } [StringLength(100)] public string Name { get; set; } = string.Empty; [StringLength(254)] public string? Email { get; set; } public List<Order> Orders { get; set; } = new List<Order>(); } public class Order { public long Id { get; set; } public long CustomerId { get; set; } [ForeignKey(nameof(CustomerId))] public Customer? Customer { get; set; } public DateOnly Date { get; set; } public List<OrderLine> Lines { get; set; } = []; } public class OrderLine { public long Id { get; set; } public long OrderId { get; set; } [ForeignKey(nameof(OrderId))] public Order? Order { get; set; } [StringLength(100)] public string Item { get; set; } = string.Empty; public decimal Amount { get; set; } }
对应DTO定义
public class CustomerDto { public long Id { get; set; } public string Name { get; set; } = string.Empty; } public class OrderDto { public long Id { get; set; } public long CustomerId { get; set; } public CustomerDto? Customer { get; set; } public DateOnly Date { get; set; } public List<OrderLineDto> Lines { get; set; } = []; } public class OrderLineDto { public long Id { get; set; } public long OrderId { get; set; } public string Item { get; set; } = string.Empty; public decimal Amount { get; set; } }
AutoMapper配置
public class AutoMapperProfile : Profile { public AutoMapperProfile() { CreateMap<Customer, CustomerDto>() .ReverseMap() .ForPath(o => o.Id, o => o.Ignore()); CreateMap<Order, OrderDto>() .ReverseMap() .ForPath(o => o.Id, o => o.Ignore()); CreateMap<OrderLine, OrderLineDto>() .ReverseMap() .ForPath(o => o.Id, o => o.Ignore()); } }
DI注册配置
services.AddAutoMapper(typeof(AutoMapperProfile));
查询代码及错误
查询代码:
var config = new MapperConfiguration(cfg => cfg.CreateProjection<Order, OrderDto>()); IQueryable<Order> query = db.Orders .Include(o => o.Customer) .Include(o => o.Lines); // Apply some Where criteria here var orderDtos = await query .AsNoTracking() .OrderBy(o => o.Id) .ProjectTo<OrderDto>(config) .ToListAsync();
执行时触发错误:
Unable to create a map expression from Order.Customer to CustomerDto.Customer ...
推测OrderLineDto也存在类似映射问题,尝试用ForMember修改配置但未成功,寻求解决方法。
解决方案
问题根源
你手动创建的MapperConfiguration仅定义了Order到OrderDto的投影规则,完全没有复用已在AutoMapperProfile中配置的Customer、OrderLine映射关系,导致AutoMapper无法识别嵌套对象的映射逻辑。
正确实现方式
1. 使用DI注入的IMapper实例(推荐)
既然已经通过services.AddAutoMapper完成了配置注册,直接从依赖注入中获取IMapper实例即可,无需手动构建新配置:
// 构造函数注入IMapper private readonly IMapper _mapper; public YourService(IMapper mapper) { _mapper = mapper; } // 查询方法 public async Task<List<OrderDto>> GetOrdersAsync() { IQueryable<Order> query = db.Orders; // Apply some Where criteria here var orderDtos = await _mapper.ProjectTo<OrderDto>(query) .AsNoTracking() .OrderBy(o => o.Id) .ToListAsync(); return orderDtos; }
或者保留原查询结构,传入已注册的配置提供器:
var orderDtos = await query .AsNoTracking() .OrderBy(o => o.Id) .ProjectTo<OrderDto>(_mapper.ConfigurationProvider) .ToListAsync();
2. 手动构建完整配置(不推荐)
若因特殊场景必须手动创建配置,需将所有关联的映射规则全部包含:
var config = new MapperConfiguration(cfg => { cfg.CreateMap<Customer, CustomerDto>() .ReverseMap() .ForPath(o => o.Id, o => o.Ignore()); cfg.CreateMap<OrderLine, OrderLineDto>() .ReverseMap() .ForPath(o => o.Id, o => o.Ignore()); cfg.CreateMap<Order, OrderDto>() .ReverseMap() .ForPath(o => o.Id, o => o.Ignore()); });
额外优化
使用ProjectTo时,无需调用Include——AutoMapper会自动生成包含关联数据的SQL查询,手动Include反而可能导致不必要的数据加载,直接移除Include语句即可。
内容的提问来源于stack exchange,提问作者jandos
相关产品推荐
相关产品推荐

