Страницы

Поиск по вопросам

суббота, 27 октября 2018 г.

Настройка маппинга элементов

Есть два класса, для которые нужно сначала сохранить в некоторых DTO-объектах, которые полностью повторяют структуру этих классов, а затем снова развернуть в исходное состояние:
public class Element { public ParentItem Item { get; set; } }
public class ParentItem { public Element Parent { get; set; } }
Метод, который создает исходные данные:
static Element[] CreateElements() { var element2 = new Element(); return new[] { new Element(new ParentItem(element2)), element2, new Element() }; }
Конфигурация автомаппера и маппинг созданных элементов:
Mapper.CreateMap(); Mapper.CreateMap(); Mapper.CreateMap(); Mapper.CreateMap();
var elements = CreateElements(); var mappedElements = elements .Select(_ => Mapper.Map(_, typeof(Element), typeof(ElementDto))) .OfType() .ToArray();
Далее проверяем, сохранилась ли структура элементов после маппинга:
foreach (var element in mappedElements) { Console.WriteLine(mappedElements.Any(e => e?.Item?.Parent == element)); }
Этот код трижды выводит False. Из чего следует, что для объекта element2 из метода CreateElements было сделано две копии. Первая при копировании перечня элементов, вторая при копировании ParentItem.Parent
Как нужно настроить маппинг, чтобы в ParentItem.Parent не создавалась еще одна копия элемента?


Ответ

Как выяснилось, AutoMapper подобные штуки не умеет разруливать. Т.е. копии объектов все равно будут создаваться.
Так что подобные зависимости придется разруливать руками. В данном случае можно сделать примерно следующее. Во-первых, при маппинге не копировать всю цепочку элементов, а скопировать только Element и его Item. Остальная часть цепочки копируется с предварительными проверками на дублирование элементов.
Mapper.CreateMap() .ForMember(_ => _.Element, _ => _.Ignore());
Во-вторых, нужно запоминать элементы, которые уже были скопированы и заполнять Item вручную.
static IEnumerable MapElements(Element[] elements) { var elementToDtoMap = new Dictionary();
foreach (var element in elements) { MapElement(element, null, elementToDtoMap); }
return elementToDtoMap.Select(_ => _.Value); }
static void MapElement(Element element, ItemDto parentItem, Dictionary elementToDtoMap) { ElementDto elementDto = null; if (elementToDtoMap.TryGetValue(element, out elementDto)) return;
elementDto = Mapper.Map(element); elementToDtoMap.Add(element, elementDto);
if (parentItem != null) { parentItem.Element = elementDto; }
if (element.Item != null) { MapElement(element.Item.Element, elementDto.Item, elementToDtoMap); } }
Таким образом, можно добиться желаемого результата. Код, конечно, будет сильно зависить от конкретного случая. Но общий принцип примерно такой.

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

Отправить комментарий