Search code examples
c#automapperautomapper-4

Mapping elements without creating duplicates


I have two classes:

public class Element
{
    public Item Item { get; set; }
}

public class Item
{
    public Element Element { get; set; }
}

And I have DTO with same structure for this classes. This method creates source data for mapping:

static Element[] CreateElements()
{
    var element2 = new Element();
    return new[]
    {
        new Element(new Item(element2)),
        element2,
        new Element()
    };
}

Then I configuring mapping and map elements:

Mapper.CreateMap<Element, ElementDto>();
Mapper.CreateMap<Item, ItemDto>();

var elements = CreateElements();

var mappedElements = elements
    .Select(_ => Mapper.Map(_, typeof(Element), typeof(ElementDto)))
    .OfType<ElementDto>()
    .ToArray();

After I check result of mapping:

foreach (var element in mappedElements)
{
    Console.WriteLine(mappedElements.Any(e => e?.Item?.Element == element));
}

This code shows "False" three times. It follows that the "element2" from "CreateElements" was created two copies.

The same test for the source elements will return "False True False":

foreach (var element in elements)
{
    Console.WriteLine(elements.Any(e => e?.Item?.Element == element));
}

As I need to configure the mapping so as not to duplicate elements? Is it possible?


Solution

  • This can be done manually. First, ignore the property Item to AutoMapper did not copy chain of elements:

    Mapper.CreateMap<Item, ItemDto>()
        .ForMember(_ => _.Element, _ => _.Ignore()); 
    

    Secondly, copy the chain manually with a mark viewed items:

    static IEnumerable<ElementDto> MapElements(Element[] elements)
    {
        var elementToDtoMap = new Dictionary<Element, ElementDto>();
    
        foreach (var element in elements)
        {
            MapElement(element, null, elementToDtoMap);
        }
    
        return elementToDtoMap.Select(_ => _.Value);
    }
    
    static void MapElement(Element element, ItemDto parentItem, Dictionary<Element, ElementDto> elementToDtoMap)
    {
        ElementDto elementDto = null;
        if (elementToDtoMap.TryGetValue(element, out elementDto))
                return;
    
        elementDto = Mapper.Map<ElementDto>(element);
        elementToDtoMap.Add(element, elementDto);
    
        if (parentItem != null)
        {
            parentItem.Element = elementDto;
        }
    
        if (element.Item != null)
        {
            MapElement(element.Item.Element, elementDto.Item, elementToDtoMap);
        }
    }