Automapper 展开到列表
Automapper Unflatten to List
我有需要转换为分层数据的平面表格数据,我正在尝试使用 AutoMapper
来实现这一点。这是表格 DTO 源和 master/detail 目标 类.
的草图
public class FlatDTO
{
public string Supplier { get; set; }
public string OrderNumber { get; set; }
public string ItemNumber { get; set; }
public string Amount { get; set; }
}
目标对象如下所示:
public class Order
{
public string AccountName { get; set; }
public string OrderNumber { get; set; }
List<OrderLines> OrderLines { get; set; }
}
public class OrderLines
{
public string Item { get; set; }
public string Amount { get; set; }
}
我的 Automapper 配置文件如下所示:
public class MyAutomapperProfile : Profile
{
public MyAutomaperProfile ()
{
CreateMap<FlatDto, Order>()
.ForMember(des => des.Account, opt => opt.MapFrom(src => src.Supplier))
.ReverseMap();
CreateMap<FlatDto, OrderLines>()
.ForMember(des => des.Item, opt => opt.MapFrom(src => src.Item))
.ReverseMap();
}
}
执行转换的函数:
public Order Transform (List<FlatDto> data)
{
var output = injectedFromCtorIMapper.Map<Order>(data);
//throws AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping
}
示例源数据:
Customer1 1234 Item1 200
Customer1 1234 Item2 500
Customer1 1234 Item3 4000
目标应该看起来像(实际上不是 Json 但看起来更好):
Order
{
AccountName = "Customer 1",
OrderNumber = "1234",
OrderLines [
OrderLine
{
Item: "Item1",
Amount: 200
},
OrderLine
{
Item: "Item2",
Amount: 500
}
OrderLine,
{
Item: "Item3",
Amount: 4000
}
]
}
几个问题:
使用 Automapper 是否可行,映射配置文件应该是什么样子才能支持这种情况?
如果第 2 行的值为 'Customer 2',Automapper 将如何处理?覆盖第一个值,即 wins 中的最后一个值?
Is this possible with Automapper, and what should the mapping profile look like to support this scenario?
是的,这是可能的 - 但请注意:AutoMapper 也有其局限性。并非所有转换都适合使用 AutoMapper 处理。
请注意:List<OrderLines> OrderLines { get; set; }
必须是 public
是的,我不了解所有细节,但基本上您可以创建从 IEnumerable<FlatDTO>
到 Order
的地图。我来举个例子。
例子
CreateMap<IEnumerable<FlatDTO>, Order>()
.ForMember(d => d.AccountName, o => o.MapFrom(s=> s.Select(c => c.Supplier).FirstOrDefault()))
.ForMember(d => d.OrderLines, o => o.MapFrom(s=> s.Select(c => new OrderLines()
{
Amount = c.Amount,
Item = c.ItemNumber,
})));
How would Automapper handle if row 2 had a value of 'Customer 2'? Overwrite the first value i.e. last one in wins?
这取决于您的映射逻辑。您甚至可以使用 AfterMap
或 BeforeMap
方法对这些双打进行检查 - 虽然不会使其更快。
理想情况下,您的初始集合可以映射到单个订单,因此包含单个客户。 - 否则,您将需要从 IEnumerable<FlatDTO>
到 IEnumerable<Order>
的映射 - 这确实是在挑战极限。
测试人员
static void Main(string[] args)
{
var dtos = new[]
{
new FlatDTO() {Supplier = "you", Amount = "10", ItemNumber = "1", OrderNumber = "123"},
new FlatDTO() {Supplier = "you", Amount = "12", ItemNumber = "2", OrderNumber = "234"}
};
var config = new MapperConfiguration(cfg => cfg.AddProfile<MyAutomapperProfile>());
var mapper = config.CreateMapper();
var order = mapper.Map<IEnumerable<FlatDTO>, Order>(dtos);
}
完成Stefan的编辑;这是一个示例,说明如果您需要 多个分组 ,您可以如何做到这一点。
了解您在问题中的预期结果,您希望对 AcccountName 和 OrderNumber 上的 master/detail 数据进行分组。
实际上,这可以通过映射 IEnumerable 和使用 Automapper 的“ProjectTo”扩展(来自命名空间 AutoMapper.QueryableExtensions)来实现。
在我的示例数据中,我添加了一行不同的 OrderNumber 来说明这一点。
using AutoMapper;
using AutoMapper.QueryableExtensions;
using System.Collections.Generic;
using System.Linq;
namespace AutomapperUnflatten
{
public class FlatDTO
{
public string Supplier { get; set; }
public string OrderNumber { get; set; }
public string ItemNumber { get; set; }
public string Amount { get; set; }
}
public class Order
{
public string AccountName { get; set; }
public string OrderNumber { get; set; }
public List<OrderLines> OrderLines { get; set; }
}
public class OrderLines
{
public string Item { get; set; }
public string Amount { get; set; }
}
class Program
{
static void Main(string[] args)
{
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<IEnumerable<FlatDTO>, Order>()
.ForMember(d => d.AccountName, opt => opt.MapFrom(src => src.FirstOrDefault().Supplier))
.ForMember(d => d.OrderNumber, opt => opt.MapFrom(src => src.FirstOrDefault().OrderNumber))
.ForMember(d => d.OrderLines, opt => opt.MapFrom(src =>
src.Select(s => new OrderLines() { Item = s.ItemNumber, Amount = s.Amount })));
});
var inputData = new List<FlatDTO>()
{
new FlatDTO(){ Supplier = "Customer1", OrderNumber = "1234", ItemNumber = "Item1", Amount = "200"},
new FlatDTO(){ Supplier = "Customer1", OrderNumber = "1234", ItemNumber = "Item1", Amount = "500"},
new FlatDTO(){ Supplier = "Customer1", OrderNumber = "1234", ItemNumber = "Item1", Amount = "4000"},
new FlatDTO(){ Supplier = "Customer1", OrderNumber = "9999", ItemNumber = "Item1", Amount = "4000"},
};
var result = inputData.GroupBy(c => (c.Supplier, c.OrderNumber)).Select(f => f).AsQueryable().ProjectTo<Order>(configuration);
}
}
}
我有需要转换为分层数据的平面表格数据,我正在尝试使用 AutoMapper
来实现这一点。这是表格 DTO 源和 master/detail 目标 类.
public class FlatDTO
{
public string Supplier { get; set; }
public string OrderNumber { get; set; }
public string ItemNumber { get; set; }
public string Amount { get; set; }
}
目标对象如下所示:
public class Order
{
public string AccountName { get; set; }
public string OrderNumber { get; set; }
List<OrderLines> OrderLines { get; set; }
}
public class OrderLines
{
public string Item { get; set; }
public string Amount { get; set; }
}
我的 Automapper 配置文件如下所示:
public class MyAutomapperProfile : Profile
{
public MyAutomaperProfile ()
{
CreateMap<FlatDto, Order>()
.ForMember(des => des.Account, opt => opt.MapFrom(src => src.Supplier))
.ReverseMap();
CreateMap<FlatDto, OrderLines>()
.ForMember(des => des.Item, opt => opt.MapFrom(src => src.Item))
.ReverseMap();
}
}
执行转换的函数:
public Order Transform (List<FlatDto> data)
{
var output = injectedFromCtorIMapper.Map<Order>(data);
//throws AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping
}
示例源数据:
Customer1 1234 Item1 200
Customer1 1234 Item2 500
Customer1 1234 Item3 4000
目标应该看起来像(实际上不是 Json 但看起来更好):
Order
{
AccountName = "Customer 1",
OrderNumber = "1234",
OrderLines [
OrderLine
{
Item: "Item1",
Amount: 200
},
OrderLine
{
Item: "Item2",
Amount: 500
}
OrderLine,
{
Item: "Item3",
Amount: 4000
}
]
}
几个问题:
使用 Automapper 是否可行,映射配置文件应该是什么样子才能支持这种情况?
如果第 2 行的值为 'Customer 2',Automapper 将如何处理?覆盖第一个值,即 wins 中的最后一个值?
Is this possible with Automapper, and what should the mapping profile look like to support this scenario?
是的,这是可能的 - 但请注意:AutoMapper 也有其局限性。并非所有转换都适合使用 AutoMapper 处理。
请注意:List<OrderLines> OrderLines { get; set; }
必须是 public
是的,我不了解所有细节,但基本上您可以创建从 IEnumerable<FlatDTO>
到 Order
的地图。我来举个例子。
例子
CreateMap<IEnumerable<FlatDTO>, Order>()
.ForMember(d => d.AccountName, o => o.MapFrom(s=> s.Select(c => c.Supplier).FirstOrDefault()))
.ForMember(d => d.OrderLines, o => o.MapFrom(s=> s.Select(c => new OrderLines()
{
Amount = c.Amount,
Item = c.ItemNumber,
})));
How would Automapper handle if row 2 had a value of 'Customer 2'? Overwrite the first value i.e. last one in wins?
这取决于您的映射逻辑。您甚至可以使用 AfterMap
或 BeforeMap
方法对这些双打进行检查 - 虽然不会使其更快。
理想情况下,您的初始集合可以映射到单个订单,因此包含单个客户。 - 否则,您将需要从 IEnumerable<FlatDTO>
到 IEnumerable<Order>
的映射 - 这确实是在挑战极限。
测试人员
static void Main(string[] args)
{
var dtos = new[]
{
new FlatDTO() {Supplier = "you", Amount = "10", ItemNumber = "1", OrderNumber = "123"},
new FlatDTO() {Supplier = "you", Amount = "12", ItemNumber = "2", OrderNumber = "234"}
};
var config = new MapperConfiguration(cfg => cfg.AddProfile<MyAutomapperProfile>());
var mapper = config.CreateMapper();
var order = mapper.Map<IEnumerable<FlatDTO>, Order>(dtos);
}
完成Stefan的编辑;这是一个示例,说明如果您需要 多个分组 ,您可以如何做到这一点。 了解您在问题中的预期结果,您希望对 AcccountName 和 OrderNumber 上的 master/detail 数据进行分组。 实际上,这可以通过映射 IEnumerable 和使用 Automapper 的“ProjectTo”扩展(来自命名空间 AutoMapper.QueryableExtensions)来实现。 在我的示例数据中,我添加了一行不同的 OrderNumber 来说明这一点。
using AutoMapper;
using AutoMapper.QueryableExtensions;
using System.Collections.Generic;
using System.Linq;
namespace AutomapperUnflatten
{
public class FlatDTO
{
public string Supplier { get; set; }
public string OrderNumber { get; set; }
public string ItemNumber { get; set; }
public string Amount { get; set; }
}
public class Order
{
public string AccountName { get; set; }
public string OrderNumber { get; set; }
public List<OrderLines> OrderLines { get; set; }
}
public class OrderLines
{
public string Item { get; set; }
public string Amount { get; set; }
}
class Program
{
static void Main(string[] args)
{
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<IEnumerable<FlatDTO>, Order>()
.ForMember(d => d.AccountName, opt => opt.MapFrom(src => src.FirstOrDefault().Supplier))
.ForMember(d => d.OrderNumber, opt => opt.MapFrom(src => src.FirstOrDefault().OrderNumber))
.ForMember(d => d.OrderLines, opt => opt.MapFrom(src =>
src.Select(s => new OrderLines() { Item = s.ItemNumber, Amount = s.Amount })));
});
var inputData = new List<FlatDTO>()
{
new FlatDTO(){ Supplier = "Customer1", OrderNumber = "1234", ItemNumber = "Item1", Amount = "200"},
new FlatDTO(){ Supplier = "Customer1", OrderNumber = "1234", ItemNumber = "Item1", Amount = "500"},
new FlatDTO(){ Supplier = "Customer1", OrderNumber = "1234", ItemNumber = "Item1", Amount = "4000"},
new FlatDTO(){ Supplier = "Customer1", OrderNumber = "9999", ItemNumber = "Item1", Amount = "4000"},
};
var result = inputData.GroupBy(c => (c.Supplier, c.OrderNumber)).Select(f => f).AsQueryable().ProjectTo<Order>(configuration);
}
}
}