简体   繁体   English

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

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

I have the source and destination objects like this: 我有这样的源和目标对象:

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; }
}

So, my need is to map the values of source.Product into dest , and then source.Categories into dest.Categories . 所以,我需要的是的值映射source.Productdest然后和source.Categoriesdest.Categories Is it possible with AutoMapper? AutoMapper有可能吗?

I have tried this and I was not surprised when it failed: 我试过这个,当它失败时我并不感到惊讶:

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

Here is the exception I received: 以下是我收到的例外情况:

[AutoMapperConfigurationException: Custom configuration for members is only supported for top-level individual members on a type.] [AutoMapperConfigurationException:成员的自定义配置仅支持某个类型的顶级个人成员。]

After some discussion with OP, it turns out his main need is to quickly map a child/nested object inside the source object to the flattened destination object. 在与OP讨论之后,事实证明他的主要需求是快速将源对象内的子/嵌套对象映射到展平的目标对象。 He does not want to write a mapping for every property of the destination. 他不想为目的地的每个属性编写映射。

Here is a way to achieve this: 这是实现此目的的一种方法:

  • Define a mapping Product -> ProductViewModel used to flatten the members of Product 定义映射Product - > ProductViewModel用于展平Product的成员
  • Define a mapping Category to CategoryViewModel 将映射Category定义为CategoryViewModel
  • Define a mapping ProductWithCategories -> ProductViewModel that maps the categories, and then in the aftermap, map the Product : 定义一个映射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));

Using recent versions of AutoMapper, you can do something like the following: 使用最新版本的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() is used to generate and populate the base class from the nested child[ren] of the source. ConstructUsing()用于从源的嵌套子[ren]生成和填充基类。 If you have more than one such nested child, you would need to make several mapping calls to map each of them successively onto the instance generated by the first Map() call. 如果您有多个这样的嵌套子级,则需要进行多次映射调用,将其中的每一个连续映射到第一次Map()调用生成的实例上。 The .ForAllOtherMembers() is relatively recent (if you don't have it, get a newer version of AutoMapper.) Unfortunately it's slightly unsafe as if you add destination members which will need mapping but forget to update the map, configuration validation will not catch it. .ForAllOtherMembers()是相对较新的(如果您没有它,请获取更新版本的AutoMapper。)不幸的是,它稍微不安全,就好像您添加了需要映射但忘记更新地图的目标成员,配置验证不会抓住它。

you should do like - 你应该喜欢 -

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));

But it is better to wrap those properties from ProductViewModel (props like Id ) inside another class. 但最好将ProductViewModel (像Id这样的道具)中的那些属性包装在另一个类中。 And create another map for things to work automapper way. 并创建另一个地图,使事物以自动播放方式工作。

The offending line that generates the error is 产生错误的违规行是

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

The error message is hard to understand, but it means you have to state the destination properties explicitly: 错误消息很难理解,但这意味着您必须明确声明目标属性:

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

You also have to define a mapping from Category to CategoryViewModel for 您还必须定义从CategoryCategoryViewModel的映射

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

to work: 上班:

config.CreateMap<Category, CategoryViewModel>();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM