首页 > 解决方案 > Automapper - 使 IncludeMembers() 忽略 null

问题描述

IncludeMembers()即使 object 是 ,也将始终从第一个匹配项映射null

假设我们有以下源模型:

public class Item
{
    public MovieMetadata MovieMetadata { get; set; }

    public BookMetadata BookMetadata { get; set; }
}

public class MovieMetadata
{
    public string Title { get; set; }
}

public class BookMetadata
{
    public string Title { get; set; }
}

目的地型号:

public class ItemDetail
{
    public string Title { get; set; }
}

映射配置文件:

public class ItemProfile : Profile
{
    public ItemProfile()
    {
        CreateMap<Item, ItemDetail>()
            .IncludeMembers(
            src => src.BookMetadata,
            src => src.MovieMetadata);

        CreateMap<BookMetadata, ItemDetail>();

        CreateMap<MovieMetadata, ItemDetail>();
    }
}

以及具有实例化和测试逻辑的 Program 类:

public class Program
{
    public static void Main()
    {
        //create mapper
        var configuration = new MapperConfiguration(cfg =>
        {
            cfg.AddMaps(typeof(ItemProfile));
        });
        var mapper = configuration.CreateMapper();
        
        //check if configuration valid
        mapper.ConfigurationProvider.AssertConfigurationIsValid();
        Console.WriteLine("Mapper configuration is valid");
        
        //try map book metadata
        var bookItem = new Item
        {
            BookMetadata = new BookMetadata()
            {
                Title = "book"
            },
            MovieMetadata = null
        };
        var bookItemDetail = mapper.Map<ItemDetail>(bookItem);
        bool isBookCorrectlyMapped = bookItem.BookMetadata.Title == bookItemDetail.Title;
        Console.WriteLine($"Book mapped correctly: {isBookCorrectlyMapped}");
        
        //try map movie metadata
        var movieItem = new Item
        {
            BookMetadata = null,
            MovieMetadata = new MovieMetadata()
            {
                Title = "movie"
            }
        };
        var movieItemDetail = mapper.Map<ItemDetail>(movieItem);
        bool isMovieCorrectlyMapped = movieItem.MovieMetadata.Title == movieItemDetail.Title;
        Console.WriteLine($"Movie mapped correctly: {isMovieCorrectlyMapped}");
    }
}

这是我们将看到的输出:

Mapper configuration is valid
Book mapped correctly: True
Movie mapped correctly: False

我们看到BookMetadata成功的项目映射,但MovieMetadata失败的映射。需要进行更新,以便此测试成功。

假设它失败的原因是因为项目的顺序IncludeMembers()

.IncludeMembers(
            src => src.BookMetadata,
            src => src.MovieMetadata)

这里我们有src.BookMetadata第一和src.MovieMetadata第二。在映射过程中,它会在其中找到Title字段src.BookMetadata并始终使用该值,即使src.BookMetadatanull

有没有办法跳过src.BookMetadatanull并使用 nextsrc.MovieMetadata代替?或者可能需要使用其他东西而不是IncludeMembers()

这是类似的问题:https ://github.com/AutoMapper/AutoMapper/issues/3204

您可以在此处找到上面的代码以快速重现问题:https ://dotnetfiddle.net/9YLGR6

标签: c#automapperautomapper-10

解决方案


找到了可能的解决方案。

选项 1. IValueResolver

我们可以创建自定义值解析器。此解决方案适用于我们需要从子对象映射的少量字段。因为任何字段都需要特定的值解析器。

让我们创建TitleResolver

public class TitleResolver : IValueResolver<Item, ItemDetail, string>
{
    public string Resolve(Item source, ItemDetail destination, string destMember, ResolutionContext context)
    {
        if (source != null)
        {
            if (source.BookMetadata != null)
            {
                return source.BookMetadata.Title;
            }
            if (source.MovieMetadata != null)
            {
                return source.MovieMetadata.Title;
            }
        }
        return null;
    }
}

并更新ItemProfile

public class ItemProfile : Profile
{
    public ItemProfile()
    {
        CreateMap<Item, ItemDetail>()
            .ForMember(dest => dest.Title, opt => opt.MapFrom<TitleResolver>());
    }
}

链接到整个代码示例:https ://dotnetfiddle.net/6pfKYh

选项 2.BeforeMap()/AfterMap()

如果我们的子对象有多个字段要映射到目标对象,那么使用BeforeMap()AfterMap()方法可能是一个好主意。在这种情况下,ItemProfile将更新为:

public class ItemProfile : Profile
{
    public ItemProfile()
    {
        CreateMap<Item, ItemDetail>(MemberList.None)
            .AfterMap((src, dest, ctx) => 
            {
                if (src.BookMetadata != null)
                {
                    ctx.Mapper.Map(src.BookMetadata, dest);
                }
                else if (src.MovieMetadata != null)
                {
                    ctx.Mapper.Map(src.MovieMetadata, dest);
                }
            });

        CreateMap<BookMetadata, ItemDetail>();

        CreateMap<MovieMetadata, ItemDetail>();
    }
}

链接到整个代码示例:https ://dotnetfiddle.net/ny1yRU


推荐阅读