具有根属性的 AutoMapper 第三级子集合映射
AutoMapper third level subcollection map with root properties
我正在尝试将源子集合映射到有效的目标子集合。但我还需要在子集合上映射一些基本属性。我尝试使用 IncludeMembers 但没有成功。我试图找到一些将两种类型映射到一种类型的示例,但这似乎不适用于我的情况,因为当我的所有实例都嵌套在同一个对象中时,大多数示例将不同类型的两个实例转换为一个。
我准备了一个单元测试来展示我的问题:
对象
public class FlatProduct
{
public string Sku { get; set; }
public int BrandId { get; set; }
public int CategoryId { get; set; }
public int ProductVersionId { get; set; }
public int PriceLevelId { get; set; }
public decimal Price { get; set; }
public decimal RebatePrice { get; set; }
public List<FlatProductInventory> FlatProductInventory { get; set; }
}
public class FlatProductInventory
{
public int StockedLocationId { get; set; }
public int StockedQty { get; set; }
}
public class Product
{
public string Sku { get; set; }
public int BrandId { get; set; }
public int CategoryId { get; set; }
public ProductVersion ProductVersion { get; set; }
}
public class ProductVersion
{
public string Sku { get; set; }
public int BrandId { get; set; }
public int CategoryId { get; set; }
public int ProductVersionId { get; set; }
public ProductPrice ProductPrice { get; set; }
public List<ProductInventory> ProductInventory { get; set; }
}
public class ProductPrice
{
public string Sku { get; set; }
public int BrandId { get; set; }
public int CategoryId { get; set; }
public int ProductVersionId { get; set; }
public int PriceLevelId { get; set; }
public decimal Price { get; set; }
public decimal RebatePrice { get; set; }
}
public class ProductInventory
{
public string Sku { get; set; }
public int BrandId { get; set; }
public int CategoryId { get; set; }
public int ProductVersionId { get; set; }
public int StockedLocationId { get; set; }
public int StockedQty { get; set; }
}
Mapping/Test
public static void TestMapper()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<FlatProduct, ProductPrice>();
cfg.CreateMap<FlatProduct, ProductVersion>();
cfg.CreateMap<FlatProduct, ProductInventory>();
cfg.CreateMap<FlatProductInventory, ProductInventory>();
cfg.CreateMap<FlatProduct, ProductVersion>()
.ForMember(productVersion => productVersion.ProductPrice, flatProduct => flatProduct.MapFrom(fp => fp))
.ForMember(productVersion => productVersion.ProductInventory, flatProduct => flatProduct.MapFrom(fp => fp))
.ForMember(productVersion => productVersion.ProductInventory, flatProduct => flatProduct.MapFrom(fp => fp.FlatProductInventory));
cfg.CreateMap<FlatProduct, Product>()
.ForMember(product => product.ProductVersion, flatProduct => flatProduct.MapFrom(fp => fp));
});
IMapper mapper = config.CreateMapper();
var flatProductExample = new FlatProduct
{
Sku = "ABC123",
BrandId = 1,
CategoryId = 1,
ProductVersionId = 1,
PriceLevelId = 1,
Price = 12.99m,
RebatePrice = 9.99m,
FlatProductInventory = new List<FlatProductInventory>()
{
new FlatProductInventory()
{
StockedLocationId = 1,
StockedQty = 33
}
}
};
var mappedProduct = mapper.Map<Product>(flatProductExample);
Assert.Equal(flatProductExample.Sku, mappedProduct.ProductVersion.ProductInventory[0].Sku); //Fail
Assert.Equal(flatProductExample.FlatProductInventory[0].StockedQty, mappedProduct.ProductVersion.ProductInventory[0].StockedQty); //Pass
}
编辑
Xerillio 非常接近我需要的答案,事实上他的答案适用于 automapper 8 以下的版本。在我的例子中,我使用的是最新版本,所以我不得不稍微修改他的代码以使其工作。这是:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<FlatProduct, ProductPrice>();
cfg.CreateMap<FlatProduct, ProductVersion>();
cfg.CreateMap<FlatProduct, ProductInventory>();
cfg.CreateMap<FlatProductInventory, ProductInventory>();
cfg.CreateMap<FlatProduct, ProductVersion>()
.ForMember(productVersion => productVersion.ProductPrice, flatProduct => flatProduct.MapFrom(fp => fp))
.ForMember(productVersion => productVersion.ProductInventory, opt => opt.MapFrom((flatProduct, productVersion, i, context) => {
return flatProduct.FlatProductInventory
// Map using '<FlatProductInventory, ProductInventory>'
.Select(flatPInv => context.Mapper.Map<FlatProductInventory, ProductInventory>(flatPInv))
// Map (in-place by passing in the destination object)
// using '<FlatProduct, ProductInventory>'
.Select(destPInv => context.Mapper.Map<FlatProduct, ProductInventory>(flatProduct, destPInv))
// Now you've combined the mappings from FlatProduct and FlatProductInventory
.ToList();
}));
cfg.CreateMap<FlatProduct, Product>()
.ForMember(product => product.ProductVersion, flatProduct => flatProduct.MapFrom(fp => fp));
});
我的理解是,对于每个 FlatProductInventory
,您都希望有一个 ProductInventory
。但是,ProductInventory
还需要填充来自 FlatProduct
.
的属性
您实际上已经走上了“将两种类型映射为一种类型”的正确轨道,因此解决问题的一种方法如下:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<FlatProduct, ProductPrice>();
cfg.CreateMap<FlatProduct, ProductVersion>();
cfg.CreateMap<FlatProduct, ProductInventory>();
cfg.CreateMap<FlatProductInventory, ProductInventory>();
cfg.CreateMap<FlatProduct, ProductVersion>()
.ForMember(productVersion => productVersion.ProductPrice, flatProduct => flatProduct.MapFrom(fp => fp))
.ForMember(productVersion => productVersion.ProductInventory,
opt => opt.ResolveUsing((flatProduct, productVersion, i, context) => {
return flatProduct.FlatProductInventory
// Map using '<FlatProductInventory, ProductInventory>'
.Select(flatPInv => context.Mapper.Map<FlatProductInventory, ProductInventory>(flatPInv))
// Map (in-place by passing in the destination object)
// using '<FlatProduct, ProductInventory>'
.Select(destPInv => context.Mapper.Map<FlatProduct, ProductInventory>(flatProduct, destPInv))
// Now you've combined the mappings from FlatProduct and FlatProductInventory
.ToList();
})
);
cfg.CreateMap<FlatProduct, Product>()
.ForMember(product => product.ProductVersion, flatProduct => flatProduct.MapFrom(fp => fp));
});
(可能有语法错误,因为我还没有 运行 通过 IDE。)
我正在尝试将源子集合映射到有效的目标子集合。但我还需要在子集合上映射一些基本属性。我尝试使用 IncludeMembers 但没有成功。我试图找到一些将两种类型映射到一种类型的示例,但这似乎不适用于我的情况,因为当我的所有实例都嵌套在同一个对象中时,大多数示例将不同类型的两个实例转换为一个。
我准备了一个单元测试来展示我的问题:
对象
public class FlatProduct
{
public string Sku { get; set; }
public int BrandId { get; set; }
public int CategoryId { get; set; }
public int ProductVersionId { get; set; }
public int PriceLevelId { get; set; }
public decimal Price { get; set; }
public decimal RebatePrice { get; set; }
public List<FlatProductInventory> FlatProductInventory { get; set; }
}
public class FlatProductInventory
{
public int StockedLocationId { get; set; }
public int StockedQty { get; set; }
}
public class Product
{
public string Sku { get; set; }
public int BrandId { get; set; }
public int CategoryId { get; set; }
public ProductVersion ProductVersion { get; set; }
}
public class ProductVersion
{
public string Sku { get; set; }
public int BrandId { get; set; }
public int CategoryId { get; set; }
public int ProductVersionId { get; set; }
public ProductPrice ProductPrice { get; set; }
public List<ProductInventory> ProductInventory { get; set; }
}
public class ProductPrice
{
public string Sku { get; set; }
public int BrandId { get; set; }
public int CategoryId { get; set; }
public int ProductVersionId { get; set; }
public int PriceLevelId { get; set; }
public decimal Price { get; set; }
public decimal RebatePrice { get; set; }
}
public class ProductInventory
{
public string Sku { get; set; }
public int BrandId { get; set; }
public int CategoryId { get; set; }
public int ProductVersionId { get; set; }
public int StockedLocationId { get; set; }
public int StockedQty { get; set; }
}
Mapping/Test
public static void TestMapper()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<FlatProduct, ProductPrice>();
cfg.CreateMap<FlatProduct, ProductVersion>();
cfg.CreateMap<FlatProduct, ProductInventory>();
cfg.CreateMap<FlatProductInventory, ProductInventory>();
cfg.CreateMap<FlatProduct, ProductVersion>()
.ForMember(productVersion => productVersion.ProductPrice, flatProduct => flatProduct.MapFrom(fp => fp))
.ForMember(productVersion => productVersion.ProductInventory, flatProduct => flatProduct.MapFrom(fp => fp))
.ForMember(productVersion => productVersion.ProductInventory, flatProduct => flatProduct.MapFrom(fp => fp.FlatProductInventory));
cfg.CreateMap<FlatProduct, Product>()
.ForMember(product => product.ProductVersion, flatProduct => flatProduct.MapFrom(fp => fp));
});
IMapper mapper = config.CreateMapper();
var flatProductExample = new FlatProduct
{
Sku = "ABC123",
BrandId = 1,
CategoryId = 1,
ProductVersionId = 1,
PriceLevelId = 1,
Price = 12.99m,
RebatePrice = 9.99m,
FlatProductInventory = new List<FlatProductInventory>()
{
new FlatProductInventory()
{
StockedLocationId = 1,
StockedQty = 33
}
}
};
var mappedProduct = mapper.Map<Product>(flatProductExample);
Assert.Equal(flatProductExample.Sku, mappedProduct.ProductVersion.ProductInventory[0].Sku); //Fail
Assert.Equal(flatProductExample.FlatProductInventory[0].StockedQty, mappedProduct.ProductVersion.ProductInventory[0].StockedQty); //Pass
}
编辑
Xerillio 非常接近我需要的答案,事实上他的答案适用于 automapper 8 以下的版本。在我的例子中,我使用的是最新版本,所以我不得不稍微修改他的代码以使其工作。这是:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<FlatProduct, ProductPrice>();
cfg.CreateMap<FlatProduct, ProductVersion>();
cfg.CreateMap<FlatProduct, ProductInventory>();
cfg.CreateMap<FlatProductInventory, ProductInventory>();
cfg.CreateMap<FlatProduct, ProductVersion>()
.ForMember(productVersion => productVersion.ProductPrice, flatProduct => flatProduct.MapFrom(fp => fp))
.ForMember(productVersion => productVersion.ProductInventory, opt => opt.MapFrom((flatProduct, productVersion, i, context) => {
return flatProduct.FlatProductInventory
// Map using '<FlatProductInventory, ProductInventory>'
.Select(flatPInv => context.Mapper.Map<FlatProductInventory, ProductInventory>(flatPInv))
// Map (in-place by passing in the destination object)
// using '<FlatProduct, ProductInventory>'
.Select(destPInv => context.Mapper.Map<FlatProduct, ProductInventory>(flatProduct, destPInv))
// Now you've combined the mappings from FlatProduct and FlatProductInventory
.ToList();
}));
cfg.CreateMap<FlatProduct, Product>()
.ForMember(product => product.ProductVersion, flatProduct => flatProduct.MapFrom(fp => fp));
});
我的理解是,对于每个 FlatProductInventory
,您都希望有一个 ProductInventory
。但是,ProductInventory
还需要填充来自 FlatProduct
.
您实际上已经走上了“将两种类型映射为一种类型”的正确轨道,因此解决问题的一种方法如下:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<FlatProduct, ProductPrice>();
cfg.CreateMap<FlatProduct, ProductVersion>();
cfg.CreateMap<FlatProduct, ProductInventory>();
cfg.CreateMap<FlatProductInventory, ProductInventory>();
cfg.CreateMap<FlatProduct, ProductVersion>()
.ForMember(productVersion => productVersion.ProductPrice, flatProduct => flatProduct.MapFrom(fp => fp))
.ForMember(productVersion => productVersion.ProductInventory,
opt => opt.ResolveUsing((flatProduct, productVersion, i, context) => {
return flatProduct.FlatProductInventory
// Map using '<FlatProductInventory, ProductInventory>'
.Select(flatPInv => context.Mapper.Map<FlatProductInventory, ProductInventory>(flatPInv))
// Map (in-place by passing in the destination object)
// using '<FlatProduct, ProductInventory>'
.Select(destPInv => context.Mapper.Map<FlatProduct, ProductInventory>(flatProduct, destPInv))
// Now you've combined the mappings from FlatProduct and FlatProductInventory
.ToList();
})
);
cfg.CreateMap<FlatProduct, Product>()
.ForMember(product => product.ProductVersion, flatProduct => flatProduct.MapFrom(fp => fp));
});
(可能有语法错误,因为我还没有 运行 通过 IDE。)