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

使用AutoMapper映射含嵌套DTO的PreSalesDTO至实体类遇阻求助

解决.NET7中AutoMapper嵌套DTO映射及多表存储问题

1. 明确DTO与实体结构(示例)

先梳理清晰你的DTO和实体类结构,以下是对应场景的示例代码:

DTO类

public class PreSalesDTO
{
    public int Id { get; set; }
    public string ProjectName { get; set; }
    // 两个嵌套DTO
    public CustomerInfoDTO Customer { get; set; }
    public ProductReqDTO ProductRequest { get; set; }
}

public class CustomerInfoDTO
{
    public string Name { get; set; }
    public string Phone { get; set; }
}

public class ProductReqDTO
{
    public string ProductType { get; set; }
    public int Quantity { get; set; }
}

实体类(对应不同数据表)

public class PreSales
{
    public int Id { get; set; }
    public string ProjectName { get; set; }
    // 外键字段
    public int CustomerId { get; set; }
    public int ProductRequestId { get; set; }
    // EF Core导航属性
    public Customer Customer { get; set; }
    public ProductRequest ProductRequest { get; set; }
}

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Phone { get; set; }
}

public class ProductRequest
{
    public int Id { get; set; }
    public string ProductType { get; set; }
    public int Quantity { get; set; }
}

2. 配置AutoMapper映射规则

创建自定义Profile类,先配置嵌套DTO与实体的映射,再配置主DTO到主实体的映射:

public class PreSalesProfile : Profile
{
    public PreSalesProfile()
    {
        // 先配置嵌套DTO到对应实体的映射
        CreateMap<CustomerInfoDTO, Customer>();
        CreateMap<ProductReqDTO, ProductRequest>();

        // 配置主DTO到主实体的映射
        CreateMap<PreSalesDTO, PreSales>()
            // 自动映射导航属性(依赖上面的嵌套映射规则)
            .ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer))
            .ForMember(dest => dest.ProductRequest, opt => opt.MapFrom(src => src.ProductRequest))
            // 外键字段暂时忽略,后续手动赋值(因为需要先保存嵌套实体获取Id)
            .ForMember(dest => dest.CustomerId, opt => opt.Ignore())
            .ForMember(dest => dest.ProductRequestId, opt => opt.Ignore());
    }
}

3. 注册AutoMapper到DI容器

在Program.cs中注册AutoMapper,确保能通过依赖注入获取IMapper实例:

var builder = WebApplication.CreateBuilder(args);
// 注册AutoMapper,指定包含Profile的程序集
builder.Services.AddAutoMapper(typeof(PreSalesProfile));

4. 实现多表存储逻辑

因为要将数据存入不同数据表,需按先保存嵌套实体,再保存主实体的顺序执行,确保外键值正确:

public class PreSalesService
{
    private readonly IMapper _mapper;
    private readonly AppDbContext _dbContext;

    public PreSalesService(IMapper mapper, AppDbContext dbContext)
    {
        _mapper = mapper;
        _dbContext = dbContext;
    }

    public async Task SavePreSalesAsync(PreSalesDTO dto)
    {
        // 映射嵌套DTO到实体
        var customer = _mapper.Map<Customer>(dto.Customer);
        var productRequest = _mapper.Map<ProductRequest>(dto.ProductRequest);

        // 先保存嵌套实体到对应数据表,获取自增Id
        _dbContext.Customers.Add(customer);
        _dbContext.ProductRequests.Add(productRequest);
        await _dbContext.SaveChangesAsync();

        // 映射主DTO到主实体,赋值外键
        var preSales = _mapper.Map<PreSales>(dto);
        preSales.CustomerId = customer.Id;
        preSales.ProductRequestId = productRequest.Id;

        // 保存主实体
        _dbContext.PreSales.Add(preSales);
        await _dbContext.SaveChangesAsync();
    }
}

5. 常见问题排查

  • 属性不匹配:如果DTO与实体属性名称不一致,需在对应映射规则中用ForMember指定映射关系,例如:
    CreateMap<CustomerInfoDTO, Customer>()
        .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.CustName));
    
  • 嵌套对象为Null:若DTO中嵌套属性可能为Null,可配置默认值避免映射异常:
    CreateMap<PreSalesDTO, PreSales>()
        .ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer ?? new CustomerInfoDTO()));
    
  • 映射未生效:检查Profile是否被正确注册,确保AddAutoMapper方法包含了该Profile所在的程序集。
  • 外键约束错误:确保EF Core实体间的关系配置正确,外键字段与导航属性的映射符合数据库约束。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 21:37:22