如何在.NET 6 Entity Framework Core中使用AutoMapper进行相关结构?

问题描述 投票:0回答:1

我正在尝试在我的项目中实现 AutoMapper。它通过在服务中使用 DTO 来正常运行。我能够将 AutoMapper 实现为简单的 DTO,但是我不知道如何将其实现为通过多个实体之间的关系进行导航的 DTO。请耐心听我解释其中的关系:

我有一个

Artist
实体。该艺术家可能在乐队中演奏,并且通过
Band
表连接到
Role
实体。在
Role
表中,我们有艺术家和乐队实体,并且该特定乐队中艺术家演奏的乐器存储在名为
RoleInstruments
的表中。我有一个名为
ArtistRoleDto
的 DTO,我将其用于多种目的。该 DTO 包含艺术家、乐队和乐器信息。有时我用它来查找单个乐队中的多个艺术家,有时我用它来查找同一艺术家的多个角色(哪个乐器在哪个乐队中演奏)。

用例的多样性让我感到困惑,我不知道如何在定义映射器时浏览必要的关系表。

这是我希望完成的代码。

CreateMap<ArtistRoleDto, Artist>()
                .ForMember(
                    dest => dest.Id,
                    opt => opt.MapFrom(src => $"{src.ArtistId}")
                    )
                .ForMember(
                    dest => dest.FirstName,
                    opt => opt.MapFrom(src => $"{src.FirstName}")
                    )
                .ForMember(
                    dest => dest.LastName,
                    opt => opt.MapFrom(src => $"{src.LastName}")
                    )
                .ForMember(
                    dest => dest.Genre,
                    opt => opt.MapFrom(src => $"{src.ArtistGenre}")
                    )
                .ForMember(
                    // BandId - through Roles
                    )
                .ForMember(
                    // BandName - through Roles.Band
                    )
                .ForMember(
                    // BandGenre - through Roles.Band
                    )
                .ForMember(
                    // Instrument - through Roles.RoleInstruments (both lists)
                    );

以下是相关实体的定义:

public class Artist
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Genre { get; set; }
    public string? Description { get; set; }

    public ICollection<Role> Roles { get; set; }
}

public class Role
{
    [Key]
    public int Id { get; set; }

    // Foreign Key to Artist Table
    public int ArtistId { get; set; }
    public Artist Artist { get; set; }

    // Foreign Key to Band Table
    public int BandId { get; set; }
    public Band Band { get; set; }

    // Additional properties related to the role, e.g., RoleDescription
    public string? Description { get; set; }

    // Collection of RoleInstruments associated with the role
    public ICollection<RoleInstruments> RoleInstruments { get; set; }
}

public class RoleInstruments
{
    // Foreign Key to Role Table
    public int RoleId { get; set; }
    public Role Role { get; set; }

    // Foreign Key to Instrument Table
    public int InstrumentId { get; set; }
    public Instrument Instrument { get; set; }
}

我尝试了不同的方法,但老实说我感觉很无能。我首先想到的是:

            .ForMember(
                dest => dest.Roles.Select(r => r.BandId),
                opt => opt.MapFrom(src => $"{src.BandId}")
                )
            .ForMember(
                // BandName - through Roles.Band
                dest => dest.Roles.Select(r => r.Band.Name),
                opt => opt.MapFrom(src => $"{src.BandName}")
                )
            .ForMember(
                // BandGenre - through Roles.Band
                dest => dest.Roles.Select(r => r.Band.Genre),
                opt => opt.MapFrom(src => $"{src.BandGenre}")
                )
            .ForMember(
                // Instrument - Through Roles.RoleInstruments (both lists)
                dest => dest.Roles.SelectMany(r => r.RoleInstruments.Select(ri => ri.Instrument.Name),
                opt => opt.MapFrom(src => $"{src.Instrument}")
                ));

然后我尝试了我心爱的 ChatGPT 的一些推荐,我得到的最接近的是:

.ForMember(
    dest => dest.Roles,
    opt => opt.MapFrom(src => new List<Role>
    {
        new Role
        {
            BandId = src.BandId,
            Band = new Band
            {
                Name = src.BandName,
                Genre = src.BandGenre
            },
            RoleInstruments = src.Instrument.Split(", ").Select(instrumentName => new RoleInstruments
            {
                Instrument = new Instrument
                {
                 // Here I get an error saying I can't convert char (instrumentName) to str.
                    Name = instrumentName 
                }
            }).ToList()

所以我现在认为解决方案可能包括在配置文件中创建一个新的

Role
? 或者考虑到 ArtistRoleDto 在各种上下文中使用,也许我应该在不同的配置文件中定义不同的映射器?当在乐队服务中使用它时,我可以按照 BandProfile 中定义的那样使用它吗?

我感谢所有花时间调查这个问题的人。

c# asp.net-core .net-core entity-framework-core automapper
1个回答
0
投票

因此,这个问题的解决方案实际上是手动映射这个特定对象,正如问题评论中所建议的那样。这是因为关系太复杂,无法使用 AutoMapper 来处理。然而,当使用 AutoMapper 遇到类似问题时,我发现为了在关系中移动时不遇到问题,我所要做的就是在定义要在服务中映射的对象时包含属性。例如:

public async Task<List<RoleInBandDto>> GetRoles(int bandId)
{
    var roleInstruments = await _context.RoleInstruments
       .Where(ri => ri.Role.Band.Id == bandId)
       .Include(ri => ri.Instrument)
       .Include(ri => ri.Role).ThenInclude(r => r.Artist)
       .Include(ri => ri.Role).ThenInclude(r => r.Band).ToListAsync();
    var roles = _mapper.Map<List<RoleInBandDto>>(roleInstruments);

    return roles;
}

我确保包含 AutoMapper 必须导航的所有表,这些表是在此处定义的:

CreateMap<RoleInstruments, RoleInBandDto>()
    .ForMember(
        dest => dest.BandName,
        opt => opt.MapFrom(src => src.Role.Band.Name))
    .ForMember(
        dest => dest.ArtistFirstName,
        opt => opt.MapFrom(src => src.Role.Artist.FirstName))
    .ForMember(
        dest => dest.ArtistLastName,
        opt => opt.MapFrom(src => src.Role.Artist.LastName))
    .ForMember(
        dest => dest.Instrument,
        opt => opt.MapFrom(src => src.Role.RoleInstruments.Select(ri => ri.Instrument.Name)));
© www.soinside.com 2019 - 2024. All rights reserved.