首页 > 解决方案 > AutoMapper - 不使用映射配置文件的相同类型的映射集合

问题描述

我在将相同类型对象的集合映射到AutoMapper. 让我给你举个例子 :

一、对象类:

public class ClassA
{
     public string Name { get; set; }
     public string Type { get; set; }
}

public class ClassB
{
     public string Name { get; set; }
     public List<ClassA> Classes { get; set; }
}

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<ClassA, ClassA>()
            .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name))
            .ForAllOtherMembers(opts => opts.Ignore());

        CreateMap<ClassB, ClassB>()
            .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name))
            .ForMember(dest => dest.Classes, opts => opts.MapFrom(src => src.Classes))
            .ForAllOtherMembers(opts => opts.Ignore());
    }
}

现在在执行这样的代码时:

 List<ClassB> targetList;

 targetList = DbContext.ClassesB.ProjectTo<ClassB>(Mapper.ConfigurationProvider).ToList();

映射无法正常工作。ClassB.Name被正确映射,但看起来映射定义ClassA被忽略了,因为所有属性都被映射了。此外,当我将ClassB.Classes属性更改为 no-list ( ClassA) 时,映射工作正常。

忽略定义的映射是错误的吗AutoMapper

标签: c#.net-coreautomapper

解决方案


如果您查看从 生成的表达式ProjectTo<ClassB>,您可以看到映射实际上是如何进行的。

var expression = DbContext.ClassesB.ProjectTo<ClassB>(Mapper.ConfigurationProvider).Expression;
Console.WriteLine(expression);

结果:

[Microsoft.EntityFrameworkCore.Query.QueryRootExpression]
    .Select(dtoClassB => new ClassB() {Classes = dtoClassB.Classes, Name = dtoClassB.Name})

在这里,List<ClassA>不是逐项映射,而是简单地分配给目标对象。我不知道这是故意设计的还是一个错误。

使用静态 API,您可以使用以下配置解决问题:

CreateMap<ClassA, ClassA>()
    .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name))
    .ForAllOtherMembers(opts => opts.Ignore());

CreateMap<ClassB, ClassB>()
    .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name))
    .ForMember(dest => dest.Classes, opts => opts.MapFrom(src => src.Classes.Select(a => Mapper.Map<ClassA>(a)).ToList()))
    .ForAllOtherMembers(opts => opts.Ignore());

Instance API 的变通方法不是很实用也很麻烦,但我还是把它带到这里。您必须创建 2 个配置文件,其中一个仅用于映射ClassA

public class MappingClassAProfile : Profile
{
    public MappingClassAProfile()
    {
        CreateMap<ClassA, ClassA>()
            .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name))
            .ForAllOtherMembers(opts => opts.Ignore());
    }
}

public class MappingProfile : MappingClassAProfile
{
    public MappingProfile(IMapper mapper) : base()
    {
        CreateMap<ClassB, ClassB>()
            .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name))
            .ForMember(dest => dest.Classes, opts => opts.MapFrom(src => src.Classes.Select(a => mapper.Map<ClassA>(a)).ToList()))
            .ForAllOtherMembers(opts => opts.Ignore());
    }
}

然后按如下方式构建映射器:

var configurationBase = new MapperConfiguration(cfg =>
{
    cfg.AddProfile<MappingClassAProfile>();
});

var mapperA = configurationBase.CreateMapper();

var configuration = new MapperConfiguration(cfg =>
{
    cfg.AddProfile(new MappingProfile(mapperA));
});

var mapper = configuration.CreateMapper(); // use it to project ClassB instances

推荐阅读