2

Image a Person and a Group class with a many-to-many relationship. A person has a list of groups and a group has a list of people.

When mapping Person to PersonDTO I have a stack overflow exception because AutoMapper can't handle the Person>Groups>Members>Groups>Members>...

So here's the example code:

public class Person
{
    public string Name { get; set; }
    public List<Group> Groups { get; set; }
}

public class Group
{
    public string Name { get; set; }
    public List<Person> Members { get; set; }
}

public class PersonDTO
{
    public string Name { get; set; }
    public List<GroupDTO> Groups { get; set; }
}

public class GroupDTO
{
    public string Name { get; set; }
    public List<PersonDTO> Members { get; set; }
}

When I use .ForMember in creating a mapper, the first mapper that gets executed throws a null reference exception.

Here's the code for the mapper:

CreateMap<Person, PersonDTO>()
    .ForMember(x => x.Groups.Select(y => y.Members), opt => opt.Ignore())
    .ReverseMap();

CreateMap<Group, GroupDTO>()
    .ForMember(x => x.Members.Select(y => y.Groups), opt => opt.Ignore())
    .ReverseMap();

So what am I missing or doing wrong? When I remove the .ForMember methods, the null reference exception is not thrown anymore.

UPDATE: I really want to emphasize the main point of my question is how to ignore a property of a property. This code is just a rather simple example.

UPDATE 2: This is how I fixed it, big thanks to Lucian-Bargaoanu

CreateMap<Person, PersonDTO>()
    .ForMember(x => x.Groups.Select(y => y.Members), opt => opt.Ignore())
    .PreserveReferences() // This is the solution!
    .ReverseMap();

CreateMap<Group, GroupDTO>()
    .ForMember(x => x.Members.Select(y => y.Groups), opt => opt.Ignore())
    .PreserveReferences() // This is the solution!
    .ReverseMap();

Thanks to .PreserveReferences() the circular references get fixed!

Mason
  • 1,007
  • 1
  • 13
  • 31

2 Answers2

2

This should just work. See https://github.com/AutoMapper/AutoMapper/wiki/5.0-Upgrade-Guide#circular-references. There is also a PR pending https://github.com/AutoMapper/AutoMapper/pull/2233.

Lucian Bargaoanu
  • 3,336
  • 3
  • 14
  • 19
  • Yes! Oh my gawd that fixed it! Thank you so much! I hoped I would not have to use a lot of ugly code, but wow just adding `.PreserveReferences()` fixed it! Thank you again. – Mason Jul 25 '17 at 11:57
  • 2
    A link to a potential solution is always welcome, but please [add context around the link](//meta.stackoverflow.com/a/8259) so your fellow users will have some idea what it is and why it’s there. **Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline.** Take into account that being _barely more than a link to an external site_ is a possible reason as to [Why and how are some answers deleted?](//stackoverflow.com/help/deleted-answers). – kayess Jul 25 '17 at 12:01
1

I think the problem you are experiencing comes from wrong assumption that Groups in PersonDTO.Groups are the same as GroupDTO - it cannot be so without the infinite dependency loop. The following code should work for you:

CreateMap<Person, PersonDTO>()
    .ForMember(x => x.Groups, opt => opt.Ignore())
    .ReverseMap()
    .AfterMap((src, dest) => 
    {
        dest.Groups = src.Groups.Select(g => new GroupDTO { Name = g.Name }).ToList()
    });

CreateMap<Group, GroupDTO>()
    .ForMember(x => x.Members, opt => opt.Ignore())
    .ReverseMap()
    .AfterMap((src, dest) => 
    {
        dest.Members = src.Members.Select(p => new PersonDTO { Name = p.Name }).ToList()
    });

You basically need to teach AutoMapper that in case of PersonDTO.Groups property it should map GroupDTO objects differently.

But I think that your problem is more like architectural issue than code one. PersonDTO.Groups should not be of type GroupDTO - you are here only interested in groups particular user belongs to and not other members of his groups. You should have some simpler type like:

public class PersonGroupDTO
{
    public string Name { get; set; }
}

(the name is up to you of course) to only identify the group without passing additionally members.

mr100
  • 4,340
  • 2
  • 26
  • 38
  • Thank you for your answer. That was I way I tried it, but it just didn't look so clean having to make a seperate DTO just to fix that issue. I updated my question with a very simple solution to fix circular references with automapper. – Mason Jul 25 '17 at 12:02
  • 1
    Only take into account that using PreserveReferences slows down automapper to some degree :) Except of that you are fine. – mr100 Jul 25 '17 at 13:16