You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 10:52:34