Сопоставление базового класса с конкретной реализацией для коллекций

#c# #.net #automapper

#c# #.net #automapper

Вопрос:

Возможно ли сопоставить исходный объект с конкретной реализацией целевого объекта с помощью automapper?

В следующем примере кода я хочу сопоставить from SourceItem с to TargetSampleItem , а не с to TargetBaseItem . Важно: TargetBaseItem не может быть абстрактным. Но если я использую следующую конфигурацию mapper, всегда TargetBaseItem используется.

Подводя итог, следующая коллекция должна содержать элементы типа TargetSampleItem , который выводится из TargetBaseItem :

 public IList<TargetItemBase> Items { get; set; } = new List<TargetItemBase>();
  

Полный код

 using AutoMapper;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MapperTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var configuration = new MapperConfiguration(cfg =>
            {
                cfg.CreateMap<SourceRoot, TargetRoot>();

                cfg.CreateMap<SourceItem, TargetItemBase>()
                    .IncludeAllDerived();
            });

            configuration.AssertConfigurationIsValid();
            var mapper = configuration.CreateMapper();

            var source = new SourceRoot
            {
                Id = 1,
                Items = new List<SourceItem>
                {
                    new SourceItem { A = "a1", B = "b1" },
                    new SourceItem { A = "a2", B = "b2" }
                }
            };

            var result = mapper.Map<TargetRoot>(source);

            // Should retur true
            Console.WriteLine(result.Items.First() is TargetSampleItem);
            Console.ReadLine();
        }

        /// <summary>
        /// Source model
        /// </summary>
        public class SourceRoot
        {
            public int Id { get; set; }
            public IList<SourceItem> Items { get; set; } = new List<SourceItem>();
        }

        public class SourceItem
        {
            public string A { get; set; }
            public string B { get; set; }
        }

        /// <summary>
        /// Target model
        /// </summary>
        public class TargetRoot
        {
            public int Id { get; set; }

            public IList<TargetItemBase> Items { get; set; } = new List<TargetItemBase>();
        }

        public class TargetItemBase
        {
            public string A { get; set; }
        }

        public class TargetSampleItem : TargetItemBase
        {
            public string B { get; set; }
        }
    }
}
  

Редактировать

использование As<> не работает, потому что чем AutoMapper не сопоставляет с типом, а не просто приводит его:

 cfg.CreateMap<SourceItem, TargetItemBase>()
   .As<TargetSampleItem>();
  

РЕДАКТИРОВАТЬ 2 / Решение

Использование As<> работает, если также добавляется сопоставление между SourceItem и TargetSampleItem :

 cfg.CreateMap<SourceItem, TargetItemBase>().As<TargetSampleItem>();
cfg.CreateMap<SourceItem, TargetSampleItem>();
  

Комментарии:

1. Я не понимаю, если вы создаете сопоставление между SourceItem и TargetItemBase , зачем AutoMapper создавать экземпляр TargetSampleItem ?

2. Хороший момент, я попытался применить это с помощью Include<>, но не смог найти никакого рабочего решения

Ответ №1:

As работает ли для меня:

 cfg.CreateMap<SourceItem, TargetItemBase>().As<TargetSampleItem>();
cfg.CreateMap<SourceItem, TargetSampleItem>();
  

Комментарии:

1. Отлично, это работает. Раньше у меня это не работало, потому что я забыл вторую карту: cfg.CreateMap<SourceItem, TargetSampleItem>();

Ответ №2:

Если As<> это не работает для вас, то возможным решением может быть использование AfterMap like —

 CreateMap<SourceRoot, TargetRoot>()
    .AfterMap((s, d) =>
    {
        s.Items.ToList().ForEach(p => d.TargetItems.Add(new TargetSampleItem { A = p.A, B = p.B }));
    });
  

(Это не элегантное решение, но поскольку TargetSampleItem оно не является целью ни одной из ваших карт, я не вижу никаких причин, по которым AutoMapper создавал бы его экземпляр).

Вы должны переименовать любое из Items свойств, чтобы AutoMapper не пытался сопоставить их автоматически (я переименовал класс в TargetRoot class TargetItems ). И, конечно, вам не нужно сопоставление между SourceItem и TargetItemBase .