Automapper Unflatten to List
Saya memiliki data tabular datar yang saya perlukan untuk diubah menjadi data hierarki, dan saya mencoba melakukannya AutoMapper. Berikut adalah sketsa sumber DTO tabel dan kelas tujuan 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; }
}
dan objek tujuan terlihat seperti ini:
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; }
}
Profil Automapper saya terlihat seperti ini:
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();
}
}
Fungsi melakukan Transformasi:
public Order Transform (List<FlatDto> data)
{
var output = injectedFromCtorIMapper.Map<Order>(data);
//throws AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping
}
Contoh Sumber Data:
Customer1 1234 Item1 200
Customer1 1234 Item2 500
Customer1 1234 Item3 4000
Target akan terlihat seperti (bukan Json tetapi terlihat lebih baik):
Order
{
AccountName = "Customer 1",
OrderNumber = "1234",
OrderLines [
OrderLine
{
Item: "Item1",
Amount: 200
},
OrderLine
{
Item: "Item2",
Amount: 500
}
OrderLine,
{
Item: "Item3",
Amount: 4000
}
]
}
Beberapa pertanyaan:
Apakah ini mungkin dengan Automapper, dan seperti apa tampilan profil pemetaan untuk mendukung skenario ini?
Bagaimana Automapper menangani jika baris 2 memiliki nilai 'Pelanggan 2'? Timpa nilai pertama yaitu yang terakhir dalam kemenangan?
Jawaban
Apakah ini mungkin dengan Automapper, dan seperti apa tampilan profil pemetaan untuk mendukung skenario ini?
Ya, ini mungkin - tetapi perhatikan: juga AutoMapper memiliki batasannya. Tidak semua transformasi cocok untuk ditangani dengan AutoMapper.
Perhatikan: List<OrderLines> OrderLines { get; set; }haruspublic
Saya tidak memiliki semua detailnya ya, tetapi pada dasarnya Anda dapat membuat peta dari IEnumerable<FlatDTO>hingga Order. Saya akan memberikan contoh.
Contoh
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,
})));
Bagaimana Automapper menangani jika baris 2 memiliki nilai 'Pelanggan 2'? Timpa nilai pertama yaitu yang terakhir dalam kemenangan?
Bergantung pada logika pemetaan Anda, itu tergantung. Anda bahkan dapat melakukan pemeriksaan pada ganda ini dengan menggunakan metode AfterMapatau BeforeMap- meskipun tidak membuatnya lebih cepat.
Idealnya, set awal Anda dapat dipetakan ke satu pesanan, dengan demikian, berisi satu pelanggan. - Jika tidak, Anda memerlukan pemetaan dari IEnumerable<FlatDTO>ke IEnumerable<Order>- yang benar-benar melampaui batas.
Penguji
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);
}
Untuk menyelesaikan pengeditan Stefan; berikut adalah contoh bagaimana Anda dapat melakukannya jika Anda perlu memiliki beberapa grup dengan . Memahami hasil yang Anda harapkan dalam pertanyaan Anda, Anda ingin mengelompokkan master / data detail pada AcccountName dan OrderNumber. Hal ini dapat dicapai dengan, memang, memetakan IEnumerable dan dengan menggunakan ekstensi " ProjectTo " dari Automapper (dari namespace AutoMapper.QueryableExtensions ). Dalam data sampel saya, saya menambahkan satu baris dengan OrderNumber berbeda untuk menggambarkan ini.
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);
}
}
}