Search code examples
c#automapper

Trying to use automapper to map a list of objects


I have the following code in my MappingProfile.cs: public class MappingProfile: Profile { public MappingProfile() {

        CreateMap<GraphUserDeltaApiDto, User>().ReverseMap();
    }

}

And in my code I'm doing this:

 graphClient.CheckforUserDeltas();
 var graphDtoUsers = graphClient.UserObjects;
 List<User> freshUsers = mapper.Map<List<GraphUserDeltaApiDto>>(graphDtoUsers);
                    

The error I get is:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
HResult=0x80131500 Message=Cannot implicitly convert type 'System.Collections.Generic.List<Core.Application.Contracts.DTOs.GraphUserDeltaApiDto>' to 'System.Collections.Generic.List<Core.Domain.Entities.User>'

I found this post:
Mapping Lists using Automapper

Based on that I tried to change my logic to look like this:

 var Users = mapper.Map(List<User>,List<GraphUserDeltaApiDto>>(graphDtoUsers);
                    var localSave = UpsertO3MWithLatestUserData(Users);

but now i get this error:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
  HResult=0x80131500
  Message=The best overloaded method match for 'AutoMapper.IMapperBase.Map<System.Collections.Generic.List<Core.Domain.Entities.User>,System.Collections.Generic.List<Core.Application.Contracts.DTOs.GraphUserDeltaApiDto>>(System.Collections.Generic.List<Core.Domain.Entities.User>)'

has some invalid arguments Source=System.Linq.Expressions

My understanding is that any fields with the same name will be copied from the source to the destination object. In my case, the source has way more data than the destination.
I'm not sure how to debug this.

Any tips would be appreciated.


Solution

  • You are trying to assign a List<GraphUserDeltaApiDto> to a List<User> because mapper.Map<List<GraphUserDeltaApiDto>>(graphDtoUsers); returns the value of the type specified as a template parameter.

    Map methods in AutoMapper have next signatures:

    /// <summary>
    /// Execute a mapping from the source object to a new destination object.
    /// The source type is inferred from the source object.
    /// </summary>
    /// <typeparam name="TDestination">Destination type to create</typeparam>
    /// <param name="source">Source object to map from</param>
    /// <returns>Mapped destination object</returns>
    TDestination Map<TDestination>(object source);
    
    /// <summary>
    /// Execute a mapping from the source object to a new destination object.
    /// </summary>
    /// <typeparam name="TSource">Source type to use, regardless of the runtime type</typeparam>
    /// <typeparam name="TDestination">Destination type to create</typeparam>
    /// <param name="source">Source object to map from</param>
    /// <returns>Mapped destination object</returns>
    TDestination Map<TSource, TDestination>(TSource source);
    

    So you have to use your destination type instead of source type in Map<TDestination> method:

    graphClient.CheckforUserDeltas();
    var graphDtoUsers = graphClient.UserObjects;
    List<User> freshUsers = mapper.Map<List<User>>(graphDtoUsers);
    

    And you have to use a source type as first generic parameter and a destination type as second generic parameter if you use TDestination Map<TSource, TDestination>(TSource source) method:

     var users = mapper.Map<List<GraphUserDeltaApiDto>, List<User>>(graphDtoUsers);
    

    This кгду is not specific to collections only, but applies to all conversions in the AutoMapper because it used the same methods.