首页 > 解决方案 > 使用 DTO 的 Automapper 多对多

问题描述

这是我第一次真正尝试使用 Automapper,我正在努力使用 DTO 正确映射多对多关系。

以下是模型:

public class Camp
{
    [Key]
    public long Id { get; set; }

    [Required]
    [MaxLength( 150 )]
    public string Name { get; set; }

    [Required]
    [MaxLength( 150 )]
    public string Location { get; set; }       

    [Required]
    public DateTime StartDate { get; set; }

    [NotMapped]
    public int CampYear
    {
        get => StartDate.Year;
    }    
    public bool Archived { get; set; }    
    public ICollection<Application> Applications { get; set; }    
    public ICollection<CampStaffPosition> CampStaffPositions { get; set; }
}

public class StaffPosition
{
    [Key]
    public int Id { get; set; }
    public string PositionName { get; set; }    
    public ICollection<CampStaffPosition> CampStaffPositions { get; set; }
}

public class CampStaffPosition
{
    public long CampId { get; set; }    
    public Camp Camp { get; set; }    
    public int StaffPositionId { get; set; }    
    public StaffPosition StaffPosition { get; set; }    
    public short PositionQuantity { get; set; } // Additional Info
}

我试图映射到的 DTO:

public class CampDto
{
    public long Id { get; set; }    
    public string Name { get; set; }    
    public string Location { get; set; }    
    public DateTime StartDate { get; set; }    
    public int CampYear { get; }    
    public bool Archived { get; set; }    
    public ICollection<ApplicationDto> Applications { get; set; }    
    public ICollection<StaffPositionDto> Positions { get; set; } // Through CampStaffPositions
}

public class StaffPositionDto
{
    public int Id { get; set; }    
    public string Type { get; set; }    
    public string PositionName { get; set; }    
    public short PositionQuantity { get; set; } // From CampStaffPositions
}

在阅读了其他几篇 SO 帖子并尝试按照他们的示例进行操作后,我感到很短。以下是几种不同的映射尝试:

    CreateMap<Camp, CampDto>()
        .ForMember( d => d.Positions, opt => opt.MapFrom( d => d.CampStaffPositions.Select( d => d.StaffPosition ).ToList() ) );

    CreateMap<StaffPosition, CampDto>()
        .ForMember( pr => pr.Positions, opt => opt.MapFrom( cp => cp.PositionName ) );

    CreateMap<StaffPosition, StaffPositionDto>();

    //CreateMap<StaffPosition, StaffPositionDto>()
    //    .ForMember( cr => cr.PositionQuantity, opt => opt.MapFrom( c => c.CampStaffPositions ) );

这些是我得到的最新错误(包括注释行):

Unable to create a map expression from StaffPosition.CampStaffPositions (System.Collections.Generic.ICollection`1[Server.Models.CampStaffPosition]) to StaffPositionDto.PositionQuantity (System.Int16) 
Mapping types: StaffPosition -> StaffPositionDto Server.Models.StaffPosition -> Shared.Dto.Core.StaffPositionDto 
Type Map configuration: StaffPosition -> StaffPositionDto Server.Models.StaffPosition -> Shared.Dto.Core.StaffPositionDto Destination Member: PositionQuantity

并排除注释行:

Expression of type 'System.Collections.Generic.List`1[Server.Models.StaffPosition]' cannot be used for parameter of type 'System.Linq.IQueryable`1[Server.Models.StaffPosition]' of method 'System.Linq.IQueryable`1[Shared.Dto.Core.StaffPositionDto] Select[StaffPosition,StaffPositionDto](System.Linq.IQueryable`1[Server.Models.StaffPosition], System.Linq.Expressions.Expression`1[System.Func`2[Server.Models.StaffPosition,Shared.Dto.Core.StaffPositionDto]])'

如何映射多对多以包含连接表中的附加属性,而不必在我的 DTO 中包含连接表?

标签: c#entity-framework-coreautomapperdto

解决方案


您需要展平一个复杂的对象。您在子对象中有属性,您希望将其提升一级,同时仍利用 AutoMapper 映射功能。有一种方法称为IncludeMembers()(请参阅文档),正是针对这种情况而存在的。它允许您在现有映射中为子类型重用配置,这样在从to映射时将包含在充当第二源PositionName的子对象中:StaffPositionCampStaffPositionStaffPositionDto

config.CreateMap<Camp, CampDto>()
    .ForMember(d => d.Positions, o => o.MapFrom(s => s.CampStaffPositions));
config.CreateMap<StaffPosition, StaffPositionDto>();
config.CreateMap<CampStaffPosition, StaffPositionDto>()
    .IncludeMembers(p => p.StaffPosition);
config.CreateMap<Application, ApplicationDto>();

用法:

var result = mapper.Map<List<CampDto>>(campsFromDatabase);

或使用ProjectTo()

var result = await dbContext
    .Set<Camp>()
    .ProjectTo<CampDto>(mapper.ConfigurationProvider)
    .ToListAsync();

推荐阅读