'Map nested object with Automapper in C#

I use the latest version of AutoMapper 10.1.1 in my .NET Core project. I have a simple database for learning new words in a language I want to learn. I have the following tables:

  • Words
  • WordExamples
  • WordExampleTranslation

In Words there is an ID for the word, and in the WordExamples I refer to this ID to link an example for that word. In WordExampleTranslation I have a reference to the ID of WordExamples to have a translation in my language (just to understand what the example means). Every table has a lot of columns such as CreatedAt, CreatedBy and so on.

With Entity Framework Core, I read this data based on the word ID and I want to return to the UI only the essential fields.

public IQueryable<WordExample> GetAllByWordId(long wordId)
{
    return _db.WordExamples
                .Include(c => c.Word)
                .Include(c => c.Translations)
                .Where(r => r.WordId == wordId);
}

For that, I created 2 classes for basic information

public class BaseWordExample
{
    public long LanguageId { get; set; }
    public long WordId { get; set; }
    public string Example { get; set; }
    public IEnumerable<BaseWordExampleTranslation> Translations { get; set; }
}

public class BaseWordExampleTranslation
{
    public long LanguageId { get; set; }
    public long WordId { get; set; }
    public long DestinationLanguageId { get; set; }
    public string TraslationExample { get; set; }
}

Then, I have my MappingProfile for AutoMapper

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<WordExample, BaseWordExample>()
            .ReverseMap();
        CreateMap<WordExampleTranslation, BaseWordExampleTranslation>()
            .ReverseMap();
    }
}

Then, I have an API

public async Task<IActionResult> GetAllAsync(long wordId)
{
    var list = _localDb.GetAllByWordId(wordId);
    var model = _mapper.Map<List<BaseWordExample>>(list);
    return model != null ? Ok(model) : NotFound();
}

I expect to receive a json mapped to the basic classes with all the data from WordExamples and also from its dependency table WordExampleTranslation. What I have is only the WordExamples values. The field Translations is not recognized by AutoMapper.

[
  {
    "id": 1,
    "language": 5,
    "wordId": 1,
    "example": "Eu parto para Inglaterra",
    "exampleHtml": "<i>Eu</i> <b>parto</b> para Inglaterra"
  }
]

Then, I tried to change the MappingProfile like the following

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<WordExample, BaseWordExample>()
            .ForMember(dest => dest.Translations, 
                       act => act.MapFrom(src => src.Translations))
            .ReverseMap();
        CreateMap<WordExampleTranslation, BaseWordExampleTranslation>()
            .ReverseMap();
    }
}

but in this case I get this error

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.MissingMethodException: Method not found: 'System.Collections.Generic.IEnumerable`1<WB.Domain.Base.BaseWordExampleTranslation> WB.Domain.Base.BaseWordExample.get_Translations()'.

Update: I tried to convert the IQueryable result in a list

var list = _localDb.GetAllByWordId(wordId).ToList();

and then use Mapper but, again, I have only the main object. All the data from the dependency table WordExampleTranslation are ignored.



Solution 1:[1]

Try this one

public async Task<IActionResult> GetAllAsync(long wordId)
{
    var list = _localDb.GetAllByWordId(wordId);
    var model = _mapper.Map<List<BaseWordExample>, List<WordExample>>(list);
    return model != null ? Ok(model) : NotFound();
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ggeorge