0

I have a problem when using AutoMapper and EF Core together to map navigation properties from the model to the DTO. My EF classes are:

public class Meal
{
    public DateTime Day { get; set; }

    public MealType MealType { get; set; }

    public int MealId { get; set; }
}

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

    public int MealTypeId { get; set; }
}

And the corresponding DTO classes:

public class ExistingMealDto
{
    public DateTime Day { get; set; }

    public ExistingMealTypeDto MealType { get; set; }

    public int MealId { get; set; }

    public string MealTypeName { get; set; }
}

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

    public int MealTypeId { get; set; }
}

This is my AutoMapper mapping:

config.CreateMap<DataLayer.EfClasses.MealType, ExistingMealTypeDto>();
config.CreateMap<DataLayer.EfClasses.Meal, ExistingMealDto>()
      .ForMember(x => x.MealType, x => x.MapFrom(x=>x.MealType))
      .ForMember(x => x.MealTypeName, x => x.MapFrom(y => y.MealType.Name));

I'm loading the data within a generic method that looks like this:

public IEnumerable<TDtoOut> GetAllAsDto<TIn, TDtoOut>()
        where TIn : class
    {
        var allEntities = DbContext.Set<TIn>();

        return Mapper.Map<IEnumerable<TDtoOut>>(allEntities);
    }

When calling this code, all Meal instances are loaded from the database and MealId and Day are filled correctly. However, MealType is null and therefore ExistingMealDto.MealType is null as well. I can work around this problem by explicitly calling DbContext.MealTypes.ToList(), but since the method should be generic for TIn, this is not a production solution.

How can I solve this issue? Thanks!

mu88
  • 4,156
  • 1
  • 23
  • 47
  • 1
    Use `ProjectTo`. http://docs.automapper.org/en/latest/Queryable-Extensions.html – Lucian Bargaoanu Apr 09 '20 at 16:26
  • Could you provide some generic code, please? – mu88 Apr 09 '20 at 16:30
  • 1
    If i'm reading this right all you need is for the MealType to be pulled from the database too. Am I correct? – Patrick Mcvay Apr 09 '20 at 16:31
  • Just try using the virtual modifier on your MealType property. That should enable lazy loading. Like this public virtual MealType MealType { get; set; } – Patrick Mcvay Apr 09 '20 at 16:37
  • this is a good explanation of lazy loading https://stackoverflow.com/questions/11469432/entity-framework-code-first-lazy-loading – Patrick Mcvay Apr 09 '20 at 16:47
  • @PatrickMcvay You are right, `MealType` has to be pulled from the DB. Just making the property virtual as you suggested does not solve the problem - unfortunately. – mu88 Apr 09 '20 at 17:13
  • This is the point where you need to step off the track of your generic repository. You are starting to add specific functionality to a generic class, so you should go towards a specific repository for this usecase. – Jesse de Wit Apr 10 '20 at 10:02

1 Answers1

0

For getting the related data in generic method , you can judge the Type of the passed type. The following is a test demo , you could refer to:

public IEnumerable<TIn> GetAllAsDto<TIn>()
    where TIn : class
    {
        Type typeParameterType = typeof(TIn);
        if (typeParameterType == typeof(User))
        {
            var Entities = _context.Set<User>().Include(u=>u.Orders);
            return (IEnumerable<TIn>)Entities;
        }

        else
        {
            var allEntities = _context.Set<TIn>();
            return allEntities;
        }
    }

public void Test()
{
     var data = GetAllAsDto<User>();
     var data1 = GetAllAsDto<Status>();
}

Result enter image description here

Xueli Chen
  • 11,987
  • 3
  • 25
  • 36