如何使用 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))
您还必须为
定义从 Category
到 CategoryViewModel
的映射
.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 的成员
- 定义映射
Category
到 CategoryViewModel
定义一个映射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。)不幸的是,它有点不安全,因为如果您添加需要映射但忘记更新映射的目标成员,配置验证将不会抓住它。
我有这样的源对象和目标对象:
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))
您还必须为
定义从Category
到 CategoryViewModel
的映射
.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 的成员
- 定义映射
Category
到CategoryViewModel
定义一个映射
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。)不幸的是,它有点不安全,因为如果您添加需要映射但忘记更新映射的目标成员,配置验证将不会抓住它。