AutoMapper: проблемы с отображением ImmutableHashSet - PullRequest
2 голосов
/ 01 мая 2020

Я пытаюсь Map и ReverseMap ImmutableHashSet свойство ICollection, используя AutoMapper.

. Automapper успешно отображает свойство ImmutableHashSet в ICollection, но он не может отобразить ICollection обратно на ImmutableHashSet

Вот минимальный воспроизводимый пример:

Предположим, у меня есть класс Order и OrderItem, как показано ниже:

public class Order
{
    private HashSet<OrderItem> _orderItems;

    public Order()
    {
        _orderItems = new HashSet<OrderItem>();
    }

    public ImmutableHashSet<OrderItem> OrderItems
    {
        get => _orderItems.ToImmutableHashSet();
        private set => _orderItems = value.ToHashSet();
    }

    public void AddOrderItem(OrderItem orderItem)
    {
        _orderItems.Add(orderItem);
    }

    public void RemoveOrderItem(OrderItem orderItem)
    {
        _orderItems.Add(orderItem);
    }
}

public class OrderItem
{
    public OrderItem(int orderId, string orderName)
    {
        OrderId = orderId;
        OrderName = orderName;
    }

    public int OrderId { get; private set; }


    public string OrderName { get; private set; }
}

Классы Order и OrderItem должны отображаться ниже OrderDto, а классы

public class OrderDto
{
    public ICollection<OrderItem> OrderItems { get; set; }
}

public class OrderItemDto
{

    public int OrderId { get; set; }


    public string OrderName { get; set; }
}

Класс OrderProfile ниже определяет профиль Automapper для сопоставления Order с OrderDto и наоборот.

public class OrderProfile : Profile
{
    public OrderProfile()
    {
        CreateMap<Order, OrderDto>()
            .ReverseMap();
        CreateMap<OrderItem, OrderItemDto>()
            .ReverseMap();
    }
}

public class OrderItemDto
{

    public int OrderId { get; set; }


    public string OrderName { get; set; }
}

Код для сопоставления и обратного сопоставления классов Order и OrderDto:

private static void Map()
{
    var mapper = new MapperConfiguration(cfg =>
    {
        cfg.AddProfile(new OrderProfile());
    }).CreateMapper();

    var order = new Order();
    order.AddOrderItem(new OrderItem(1, "Laptop"));
    order.AddOrderItem(new OrderItem(2, "Keyboard"));

    // This code maps correctly
    var orderDto = mapper.Map<OrderDto>(order);

    // This is where I get an exception
    var orderMappedBack = mapper.Map<Order>(orderDto);
}

При отображении Order object из OrderDto, я получаю следующее исключение:

System.ArgumentException: System.Collections.Immutable.ImmutableHashSet`1[ReadOnlyCollectionDemo.OrderItem]
needs to have a constructor with 0 args or only optional args.
(Parameter 'type') at lambda_method(Closure , OrderDto , Order , ResolutionContext )

Любые указатели, которые помогут решить эту проблему, высоко ценятся.

Обновление

У меня как-то получилось работать через кастом Converter. Но я действительно не знаю, как это работает. Я обновил свою конфигурацию маппера следующим образом:

 var mapper = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile(new OrderProfile());
            cfg.CreateMap(typeof(ICollection<>), typeof(ImmutableHashSet<>)).ConvertUsing(typeof(HashMapConverter<,>));
        }).CreateMapper();

Мой HashMapConverter класс:

public class HashMapConverter<TCollection, TImmutableHashSet> 
        : ITypeConverter<ICollection<TCollection>, ImmutableHashSet< TImmutableHashSet>>
    {
        public ImmutableHashSet< TImmutableHashSet> Convert(
            ICollection<TCollection> source,
            ImmutableHashSet< TImmutableHashSet> destination,
            ResolutionContext context)
        {
            return ImmutableHashSet<TImmutableHashSet>.Empty;
        }
    }

Как вы видите, я просто возвращаю пустой ImmutableHashSet, однако, к моему удивлению, OrderDto теперь успешно сопоставлен с Order с правильным номером OrderItems Count. Я бы ожидал, что Count будет 0, так как я возвращаю пустое HashSet. Я подозреваю, что это работает, потому что OrderItem и OrderItemDto также отображаются через AutoMapper.

Я хотел бы подтвердить свою гипотезу и узнать, является ли это правильным подходом.

Ответы [ 2 ]

2 голосов
/ 06 мая 2020

В моем аналогичном примере я получил не ожидаемые значения, а пустую коллекцию, поэтому вам лучше завершить функцию преобразования с помощью

public class HashMapConverter<TCollection, TImmutableHashSet>
: ITypeConverter<ICollection<TCollection>, ImmutableHashSet<TImmutableHashSet>>
{
    public ImmutableHashSet<TImmutableHashSet> Convert(
        ICollection<TCollection> source,
        ImmutableHashSet<TImmutableHashSet> destination,
        ResolutionContext context)
    {
        var hs =new HashSet<TCollection>(source);
        return ((IEnumerable<TImmutableHashSet>)hs).ToImmutableHashSet<TImmutableHashSet>();
        //return ImmutableHashSet<TImmutableHashSet>.Empty;
    }
}

вместо возврата пустого;

0 голосов
/ 12 мая 2020

Я ждал истечения срока действия щедрости, прежде чем писать этот ответ.

Как оказалось, отображение ImmutableHashSet с коллекцией было не так просто, как кажется. Однако это заставило меня понять, что, возможно, я не решал правильную проблему. Все, что я хотел, это убедиться, что мой OrderItem HashSet не может быть изменен вне класса Order. Я мог бы легко добиться этого, вернув IReadOnlyCollection.

Вот мой обновленный Order класс:

public class Order
{
    private HashSet<OrderItem> _orderItems;

    public Order()
    {
        _orderItems = new HashSet<OrderItem>();
    }

    public IReadOnlyCollection<OrderItem> OrderItems
    {
        get => _orderItems.ToImmutableHashSet();
        private set => _orderItems = value.ToHashSet();
    }

    public void AddOrderItem(OrderItem orderItem)
    {
        _orderItems.Add(orderItem);
    }

    public void RemoveOrderItem(OrderItem orderItem)
    {
        _orderItems.Add(orderItem);
    }
}

IReadOnlyCollection не только помогает мне решить проблему Automapper, но в дополнение к этому, он не раскрывает метод Add , Следовательно, пользователь не может случайно вызвать метод order.OrderItems.Add(orderItem) вне класса Order.

...