AutoMapper error mapping from an IEnumerable<T> where T is an interface

1.2k views Asked by At

I have a situation where I need to flatten a collection of items.

I set my Mapping profile like this:

CreateMap<IEnumerable<IMySetting>, IMySettingLookup>()
    .ForMember(l => l.MySetting1,
        o => o.MapFrom(sc =>
            sc.FirstOrDefault(s => s.SettingName == nameof(IMySettingLookup.MySetting1)).SettingValue))
    .ForMember(l => l.MySetting2,
        o => o.MapFrom(sc =>
            sc.FirstOrDefault(s => s.SettingName == nameof(IMySettingLookup.MySetting2)).SettingValue))
    .As<MySettingLookup>(); // notice this is a concrete type

when i call the map function like this:

IEnumerable<IMySetting> settings =....;
IMySettingLookup lookup = _mapper.Map<IMySettingLookup>(settings);

i get this error:

    AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.
    
    Mapping types:
    Object -> IMySettingLookup
    System.Object -> MyStuff.Models.IMySettingLookup
  Stack Trace: 
    lambda_method(Closure , Object , IMySettingLookup, ResolutionContext )

However when I set my mapping profile using concrete types and call the Map method with the concrete type like below, everything works:

CreateMap<IEnumerable<MySetting>, MySettingLookup>() // notice everything is concrete types
   .ForMember(...).... 



and

IEnumerable<MySetting> settings =....;
IMySettingLookup lookup = _mapper.Map<MySettingLookup>(settings); // notice concrete types

My Question is how can I define my mapping profiles when my source type is an IEnumerable<T> where T is an interface?

EDIT

I have uploaded a minimal reproducible example to https://github.com/nandun5/so-68219262-automapper

Note that I won't see any issues if i simply use concrete classes instead of the interfaces. however this is not really an option for me in my actual scenario because all of my data service contracts are interfaces.

1

There are 1 answers

7
ProgrammingLlama On

It seems that .As<MyType> doesn't give the concrete type to use, rather it tells AutoMapper which mapping to look up. This is supported by the documentation which states:

For simple cases, you can use As to redirect a base map to an existing derived map:

In your case, it's looking up a mapping for IEnumerable<MySetting> to MySettingLookup, which it isn't finding.

Therefore, you actually want to declare two mappings:

CreateMap<IEnumerable<IMySetting>, MySettingLookup>()
   .ForMember(...);

CreateMap<IEnumerable<IMySetting>, IMySettingLookup>()
   .As<MySettingLookup>();

This will then use the <IEnumerable<MySetting>, MySettingLookup> mapping when trying to map <IEnumerable<MySetting>, IMySettingLookup>.

Alternatively, you can simply replace .As<MySettingLookup>() with the following .ConstructUsing(_ => new MySettingLookup()):

CreateMap<IEnumerable<IMySetting>, IMySettingLookup>()
   .ForMember(...)
   .ConstructUsing(_ => new MySettingLookup());