如何使用 AutoMapper 将目标对象映射到源对象中的子对象?

How to use AutoMapper to map destination object with a child object in the source object?

我有这样的源对象和目标对象:

class ProductWithCategories // Source class
{
    public Product Product { get; set; } // Product is an EF entity class
    public IEnumerable<Category> Categories { get; set; }
}

class ProductViewModel // Dest class
{
    public int Id { get; set; }
    // Other properties with the same name as Product class

    public IEnumerable<CategoryViewModel> Categories { get; set; }
}

所以,我的需求是将source.Product的值映射到dest,然后将source.Categories映射到dest.Categories。 AutoMapper 可以吗?

我试过了,失败时我并不感到惊讶:

        config.CreateMap<ProductWithCategories, ProductViewModel>()
            .ForMember(q => q, option => option.MapFrom(q => q.Product))
            .ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories));

这是我收到的异常:

[AutoMapperConfigurationException: Custom configuration for members is only supported for top-level individual members on a type.]

产生错误的违规行是

.ForMember(q => q, option => option.MapFrom(q => q.Product))

错误消息难以理解,但这意味着您必须明确说明目标属性:

.ForMember(q => q.Id, option => option.MapFrom(q => q.Product.Id))
.ForMember(q => q.OtherProperty, option => option.MapFrom(q => q.Product.OtherProperty))

您还必须为

定义从 CategoryCategoryViewModel 的映射
.ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories))

上班:

config.CreateMap<Category, CategoryViewModel>();

你应该这样做 -

AutoMapper.Mapper.CreateMap<Category, CategoryViewModel>();
AutoMapper.Mapper.CreateMap<ProductWithCategories, ProductViewModel>()
     .ForMember(a => a.Id, b => b.ResolveUsing(c => c.Product != null ? c.Product.MyProperty : 0))
     .ForMember(a => a.Categories, b => b.ResolveUsing(c => c.Categories));

但是最好将 ProductViewModel 中的那些属性(像 Id 这样的道具)包装在另一个 class 中。并创建另一个地图,让事物以自动映射器的方式工作。

在与 OP 讨论后,发现他的主要需求是快速将源对象中的 child/nested 对象映射到扁平化的目标对象。他不想为每个 属性 目的地都写一个映射。

这里有一个方法可以实现:

  • 定义一个映射 Product -> ProductViewModel 用于展平 Product
  • 的成员
  • 定义映射 CategoryCategoryViewModel
  • 定义一个映射ProductWithCategories -> ProductViewModel映射类别,然后在aftermap中,映射Product:

    config.CreateMap<ProductWithCategories, ProductViewModel>() .ForMember(q => q.Id, option => option.Ignore()) // flattened in AfterMap .ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories)) .AfterMap((src, dst) => Mapper.Map(src.Product, dst));

使用最新版本的 AutoMapper,您可以执行以下操作:

config.CreateMap<Product, ProductViewModel>()
      .ForMember(q => q.Categories, option => option.Ignore());

config.CreateMap<ProductWithCategories, ProductViewModel>()
      .ConstructUsing(s => AutoMapper.Mapper.Map<ProductViewModel>(s.Product))
      .ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories))
      .ForAllOtherMembers(o => o.Ignore();

ConstructUsing() 用于从源的嵌套子 [ren] 生成和填充基础 class。如果您有多个这样的嵌套子对象,则需要进行多次映射调用以将它们中的每一个依次映射到第一个 Map() 调用生成的实例上。 .ForAllOtherMembers() 是相对较新的(如果您没有它,请获取更新版本的 AutoMapper。)不幸的是,它有点不安全,因为如果您添加需要映射但忘记更新映射的目标成员,配置验证将不会抓住它。