MaxTheCoder
MaxTheCoder

Reputation: 135

EFCore DotNet 5 & Automapper. Map virtual collections

Firstly, I am new to Automapper and finding the official documentation poor to say the least.

I am trying to map two complex entities with virtual collections which themselves need to be mapped

Map<IEnumerable<ComplexEntityDb>, IEnumerable<ComplexEntityVM>> 

within each row I need to map

Map<IEnumerable<EntityCollectionItemDb>, IEnumerable<EntityCollectionItemVM>>

A very simplified version of the classes:

public class ComplexEntityDb
{
    public int Id;

    // Multiple properties (14) removed for brevity

    public virtual ICollection<EntityCollectionItemDb> CollectionDb { get; set; }
}

public class ComplexEntityVM
{
    public int Id;

    // Multiple properties (7) removed for brevity

    public virtual ICollection<EntityCollectionItemVM> CollectionDb { get; set; }
}


public class EntityCollectionItemDb
{
    public int Id;

    // Multiple properties (12) removed for brevity
}

public class EntityCollectionItemVM
{
    public int Id;

    // Multiple properties (6) removed for brevity
}

What is the correct way to do this with EF Core 5.04 on dotnet 5 using AutoMapper 10.1.1 and DI extensions 8.1.1

I have read dozens of articles on this site and there does not seem to be an easy way to do this.

Many thanks in advance for any help much wiser souls can give.

EDIT: (taken from the comment section of @dglozano's answer) -

I am using mapping profile -

public class MappingProfile : Profile
{
    public MappingProfile() 
    {
        CreateMap<ComplexEntityDb, ComplexEntityVM>().ReverseMap();
        CreateMap<EntityCollectionItemDb, EntityCollectionItemVM>).ReverseMap(); 
    } 
} 

and IMapper -

private readonly IMapper _mapper; 

public CustomService(IMapper mapper) 
{
    _mapper = mapper;
} 

and trying -

return _mapper.Map<IEnumerable<ComplexEntityDb>, IEnumerable<ComplexEntityVM>>(source); 

Result: EntityCollectionItemVM collection is empty.

Upvotes: 0

Views: 704

Answers (1)

dglozano
dglozano

Reputation: 6607

You just need to define the mappings of each element type, as the documentation for Nested Mappings suggests. You have to tell Automapper how to map ComplexEntityDb -> ComplexEntityVM and how to map EntityCollectionItemDb -> EntityCollectionItemVM, and then all the mappings of collections of those items will automatically be supported.

So in your case, a minimal example would look like this:

using System;
using AutoMapper;
using System.Collections.Generic;

public class ComplexEntityDb
{
    public ComplexEntityDb(int id)
    {
        Id = id;
        var item1 = new EntityCollectionItemDb();
        var item2 = new EntityCollectionItemDb();
        item1.Id = id * 1000 + 1;
        item2.Id = id * 1000 + 2;
        CollectionDb = new List<EntityCollectionItemDb>{item1, item2, };
    }

    public int Id
    {
        get;
        set;
    }

    // Multiple properties (14) removed for brevity
    public ICollection<EntityCollectionItemDb> CollectionDb
    {
        get;
        set;
    }
}

public class ComplexEntityVM
{
    public int Id;
    // Multiple properties (7) removed for brevity
    public ICollection<EntityCollectionItemVM> CollectionDb
    {
        get;
        set;
    }
}

public class EntityCollectionItemDb
{
    public int Id;
// Multiple properties (12) removed for brevity
}

public class EntityCollectionItemVM
{
    public int Id;
// Multiple properties (6) removed for brevity
}

public class Program
{
    public static void Main()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<ComplexEntityDb, ComplexEntityVM>();
            cfg.CreateMap<EntityCollectionItemDb, EntityCollectionItemVM>();
        });
        var complexEntity1 = new ComplexEntityDb(1);
        var complexEntity2 = new ComplexEntityDb(2);
        var complexEntity3 = new ComplexEntityDb(3);
        var source = new List<ComplexEntityDb>{complexEntity1, complexEntity2, complexEntity3};
        var mapper = config.CreateMapper();
        var dest = mapper.Map<IEnumerable<ComplexEntityDb>, IEnumerable<ComplexEntityVM>>(source);
        
        foreach(var parentMapped in dest)
        {
            Console.WriteLine("Mapped parent Id {0}", parentMapped.Id);
            foreach(var childMapped in parentMapped.CollectionDb)
            {
                Console.WriteLine("  - Mapped child Id {0}", childMapped.Id);
            }
            Console.WriteLine();
        }
    }
}
Output:

Mapped parent Id 1
  - Mapped child Id 1001
  - Mapped child Id 1002

Mapped parent Id 2
  - Mapped child Id 2001
  - Mapped child Id 2002

Mapped parent Id 3
  - Mapped child Id 3001
  - Mapped child Id 3002

Try it out in this fiddle.

In a more real scenario, it would be the same idea, but I would create a Profile(s) to define each of the mappings configurations and then use the injected IMapper to do execute the mapping.

Upvotes: 1

Related Questions