Automapper가 목록으로 병합 해제 됨

Oct 03 2020

계층 적 데이터로 전환하는 데 필요한 플랫 테이블 형식 데이터가 있으며 AutoMapper. 다음은 표 형식 DTO 소스 및 마스터 / 세부 대상 클래스의 스케치입니다.

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
                   }
                ]
}

몇 가지 질문 :

  1. Automapper를 사용하면 가능하며이 시나리오를 지원하려면 매핑 프로필이 어떻게 표시되어야합니까?

  2. 행 2의 값이 '고객 2'인 경우 Automapper는 어떻게 처리합니까? 첫 번째 값, 즉 승리의 마지막 값을 덮어 쓰시겠습니까?

답변

1 Stefan Oct 03 2020 at 18:51

Automapper를 사용하면 가능하며이 시나리오를 지원하려면 매핑 프로필이 어떻게 표시되어야합니까?

예, 가능합니다. 그러나 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,
       })));

행 2의 값이 '고객 2'인 경우 Automapper는 어떻게 처리합니까? 첫 번째 값, 즉 승리의 마지막 값을 덮어 쓰시겠습니까?

매핑 논리에 따라 다릅니다. 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);
}
2 Reno Oct 03 2020 at 19:42

Stefan의 편집을 완료하려면 여기 에 여러 그룹 이 필요한 경우 수행 할 수있는 방법의 예가 있습니다 . 질문에서 예상되는 결과를 이해하고 AcccountName 및 OrderNumber에 대한 마스터 / 세부 정보 데이터를 그룹화하려고합니다. 이것은 실제로 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);            
        }
    }
}