Self referencing loop causing stack overflow with map

2.5k Views Asked by At

According to the documentation PreserveReferences should be set automatically whenever possible for AutoMapper.

Starting from 6.1.0 PreserveReferences is set automatically at config time whenever possible.

https://github.com/AutoMapper/AutoMapper/wiki/5.0-Upgrade-Guide

I have also tried setting MaxDepth to 1 but I still get a stack overflow exception with the following mapping. Can I get around this somehow or do I need to modify the view models?

cfg.CreateMap<ArticleViewModel, Article>(MemberList.Source)
    .MaxDepth(1)
    .EqualityComparison((src, dst) => src.Id == dst.Id);

Code that causes the stack overflow exception:

var article = await iArticleRepository.GetAsync(id);
//The mapping below causes the exception
var mappedArticle = Mapper.Map<ArticleViewModel>(article);

Entities:

public class Article: IEntity<int>
{
    [Key]
    public int Id { get; set; }

    ...

    public int SupplierId { get; set; }

    public virtual Supplier Supplier { get; set; }
}

public class Supplier: IEntity<int>
{
    [Key]
    public int Id { get; set; }

    ...

    public virtual ICollection<Contact> Contacts { get; set; }
}

public class Contact: IEntity<int>
{
    [Key]
    public int Id { get; set; }

    ...
    public virtual ICollection<Supplier> Suppliers { get; set; }
}

View models:

public class ArticleViewModel
{
    public int Id { get; set; }

    ...
    public SupplierViewModel Supplier { get; set; }

}

public class SupplierViewModel
{
    public int Id { get; set; }

    ...
    public List<ContactViewModel> Contacts { get; set; }

}

public class ContactViewModel
{
    public int Id { get; set; }
    ... 
    public List<SupplierViewModel> Suppliers { get; set; }
}
1

There are 1 best solutions below

1
On BEST ANSWER

Well, it's unclear what does whenever possible mean. Since the documentation before that states

It turns out this tracking is very expensive, and you need to opt-in using PreserveReferences for circular maps to work

looks like your scenario fails into not possible category :)

Let not rely on that and use the explicit opt-in. The circular reference in this sample model is between Supplier and Contact, so you have to specify in one of the involved class mappings, for instance:

cfg.CreateMap<ArticleViewModel, Article>(MemberList.Source)
    .MaxDepth(1)
    .EqualityComparison((src, dst) => src.Id == dst.Id);

cfg.CreateMap<SupplierViewModel, Supplier>(MemberList.Source)
    .PreserveReferences()
    .EqualityComparison((src, dst) => src.Id == dst.Id);