Automapper Unflatten to List

Oct 03 2020

Hiyerarşik verilere dönüştürmem gereken düz tablo verilerim var ve bunu yapmaya çalışıyorum AutoMapper. Burada, tablolu DTO kaynağının ve ana / ayrıntı hedef sınıflarının bir taslağı yer almaktadır.

public class FlatDTO
{
    public string Supplier { get; set; }
    public string OrderNumber { get; set; }
    public string ItemNumber { get; set; }
    public string Amount { get; set; }
}

ve hedef nesneler şuna benzer:

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 profilim şuna benziyor:

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

Dönüşümü gerçekleştiren işlev:

public Order Transform (List<FlatDto> data)
{
     var output = injectedFromCtorIMapper.Map<Order>(data);
     //throws AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping 
}

Örnek Kaynak Veriler:

Customer1    1234    Item1   200
Customer1    1234    Item2   500
Customer1    1234    Item3   4000

Hedef şöyle görünmeli (aslında Json değil ama daha iyi görünüyor):

Order
{ 
    AccountName = "Customer 1",
    OrderNumber = "1234",
    OrderLines [
                   OrderLine
                   {
                      Item: "Item1",
                      Amount: 200
                   },
                   OrderLine
                   {
                      Item: "Item2",
                      Amount: 500
                   }
                   OrderLine,
                   {
                      Item: "Item3",
                      Amount: 4000
                   }
                ]
}

Birkaç soru:

  1. Bu, Automapper ile mümkün müdür ve bu senaryoyu desteklemek için eşleme profili nasıl görünmelidir?

  2. 2. satırda 'Müşteri 2' değeri olsaydı Automapper nasıl davranırdı? İlk değerin, yani sonuncunun üzerine yazılsın mı?

Yanıtlar

1 Stefan Oct 03 2020 at 18:51

Bu, Automapper ile mümkün müdür ve bu senaryoyu desteklemek için eşleme profili nasıl görünmelidir?

Evet, bu mümkündür - ancak şunu unutmayın: AutoMapper'ın da kendi sınırlamaları vardır. Tüm dönüşümler AutoMapper ile işlenmeye uygun değildir.

Unutmayın: List<OrderLines> OrderLines { get; set; }olmalıdırpublic

Ben tüm ayrıntıları evet yok ama temelde bir harita oluşturabilir IEnumerable<FlatDTO>için Order. Bir örnek oluşturacağım.

Misal

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

2. satırda 'Müşteri 2' değeri olsaydı Automapper nasıl davranırdı? İlk değerin, yani sonuncunun üzerine yazılsın mı?

Haritalama mantığınıza bağlı olarak değişir. AfterMapVeya BeforeMapyöntemlerini kullanarak bu çiftleri kontrol edebilirsiniz - yine de daha hızlı yapmaz.

İdeal olarak, ilk setiniz tek bir siparişle eşleştirilebilir, böylece tek bir müşteri içerir. - Aksi takdirde, bir haritaya ihtiyacınız IEnumerable<FlatDTO>olacak IEnumerable<Order>- bu gerçekten sınırları zorluyor.


Test cihazı

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);
}
2 Reno Oct 03 2020 at 19:42

Stefan 'ın düzenlemesini tamamlamak için; burada birden çok gruba sahip olmanız gerektiğinde bunu nasıl yapabileceğinize dair bir örnek var . Sorunuzda beklediğiniz sonucu anlamak için, HesapAdı ve Sipariş Numarası üzerindeki ana / ayrıntı verilerini gruplamak istiyorsunuz. Bu, IEnumerable'ı eşleyerek ve Automapper'in " ProjectTo " uzantısını ( AutoMapper.QueryableExtensions ad alanından ) kullanarak elde edilebilir. Örnek verilerime bunu göstermek için farklı bir OrderNumber ile bir satır ekledim.

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