2

I'm trying to map nested ICollection of one of my models to existing Dto, but I'm struggling to Map it properly with AutoMapper

Models:

public class Ingredient : BaseEntity<long>
{
    [MaxLength(100)]
    public string Name { get; set; }
    [ForeignKey("Id")]
    public int CustomerId { get; set; }
    public bool IsPackaging { get; set; }
    public virtual ICollection<ProductIngredient> ProductIngredient { get; set; }
    public virtual ICollection<IngredientComposition> IngredientComposition { get; set; }
}

Collection Model:

public class IngredientComposition : BaseEntity<int>
{
    [MaxLength(20)]
    public string Type { get; set; }
    [MaxLength(200)]
    public string Key { get; set; }
    [MaxLength(200)]
    public string Value { get; set; }
}

Dto:

public class IngredientDto
{
    public long Id { get; set; }
    public DateTime CretedOn { get; set; }
    public DateTime UpdatedOn { get; set; }
    public string Name { get; set; }
    public int CustomerId { get; set; }
    public int UsedCount { get; set; }
    public bool IsPackaging { get; set; }
    public IList<Composition> Ingredients { get; set; }
}

public class Composition
{
    public string Type { get; set; }
    public string Key { get; set; }
    public string Value { get; set; }
}

My maps looks as follows as I'm struggling to properly set "ForMemeber" method(s):

CreateMap<Ingredient, IngredientDto>();
CreateMap<IngredientDto, Ingredient>();

Any help much appropriated! Thanks

EDIT:

This is how I'm getting data:

return await _context.Ingredients
                     .Where(i => i.CustomerId ==_userResolverService.GetCustomerId())
                     .Include(i => i.IngredientComposition)
                     .Select(i => _mapper.Map<Ingredient, IngredientDto>(i))
                     .OrderBy(i => i.Name)
                     .ToListAsync();
Soheil Alizadeh
  • 2,936
  • 11
  • 29
  • 56
sziszu
  • 490
  • 1
  • 6
  • 16

3 Answers3

3

First, you must do add CreateMap<IngredientComposition, Composition>(); and after doing this you must do change your Linq Query. You can use AutoMapper.EF6

return _context.Ingredients
                 .Where(i => i.CustomerId ==_userResolverService.GetCustomerId())
                 .Include(i => i.IngredientComposition)
                 .ProjectToList<IngredientDto>();

after use this you donot need use Select.

note: donot forget add _mapper.ConfigurationProvider in ProjectToList

ProjectToList<IngredientDto>(_mapper.ConfigurationProvider);

if you don't set it to get this Exception:

Mapper not initialized. Call Initialize with Appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance.

more detail.

Update: your properties must have the same name.if you change Dto property Ingredients to IngredientComposition don't need use ForMember.

Soheil Alizadeh
  • 2,936
  • 11
  • 29
  • 56
  • Include is not needed, by default AM fetches everything. – Lucian Bargaoanu Aug 21 '17 at 19:50
  • Yes, maybe @sziszu do use eager loading. – Soheil Alizadeh Aug 21 '17 at 19:53
  • Strange, it still returns null. Can this be caused because it's .net core? I also do not have ProjectToList, so I did it like that: return await _context.Ingredients.Where(i => i.CustomerId == _userResolverService.GetCustomerId()) .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); – sziszu Aug 21 '17 at 20:00
1

Actually this worked for me:

Query:

return await _context.Ingredients.Where(i => i.CustomerId == _userResolverService.GetCustomerId())
        .Include(sx=>sx.IngredientComposition)
        .ProjectTo<IngredientDto>()
        .ToListAsync();

Maps: First of All as you suggested, internal collection mapping then main objects mapping + ForMember which worked once internal objects were mapped

CreateMap<IngredientComposition, Composition>().ReverseMap();
CreateMap<Ingredient, IngredientDto>().ForMember(d => d.Ingredients, opt=>opt.MapFrom(c=>c.IngredientComposition)).ReverseMap();

Thanks for all help!

sziszu
  • 490
  • 1
  • 6
  • 16
0

If you make a map for the child DTO, Automapper is smart enough to figure it out without the ForMember:

CreateMap<IngredientComposition , Composition>()
    .ReverseMap();  //Reverse map tells AM to go both ways

CreateMap<Ingredient, IngredientDto>()
    .ReverseMap();

// CreateMap<IngredientDto, Ingredient>();  ** Not needed with ReverseMap()
Steve Greene
  • 12,029
  • 1
  • 33
  • 54
  • Hi unfortunately this doesn't work. I still get null instead of array on IngredientDto but Ingredient has attached collection - Look edit how I'm getting data – sziszu Aug 21 '17 at 19:32