Home > Mobile >  AutoMapper cannot create an instance of IMemberValueResolver via reflection
AutoMapper cannot create an instance of IMemberValueResolver via reflection

Time:10-25

I'm getting an error that AutoMapper v11.0.1 cannot create an instance of a custom IMemberValueResolver type.

This is my custom resolver:

public class FooCollectionResolver<TModel, TViewModel> : IMemberValueResolver<object, object, BlockListModel?, IEnumerable<TViewModel>?>
 where TModel : class, IPublishedElement
 where TViewModel : class
{
    public IEnumerable<TViewModel>? Resolve(object source, object destination, BlockListModel? sourceMember, IEnumerable<TViewModel>? destMember, ResolutionContext context)
    { }
}

It takes a source BlockListModel member and returns a collection of TViewModel.

Example usage:

this.CreateMap<FooModel, FooViewModel>().ForMember(
dest => dest.BlockListModelMember,
src => src.MapFrom<FooCollectionResolver<FooCollectionModel, FooCollectionViewModel>, BlockListModel>(s => s.BlockListModelMember));

FooModel.cs:

public class FooModel 
{
    public string Title { get; set; }
    public BlockListModel BlockListModelMember { get; set; }
}

FooViewModel.cs

public class FooViewModel 
{
   public string Title { get; set; }
   public IEnumerable<FooCollectionViewModel> BlockListModelMember { get; set; }
}

FooCollectionModel.cs

public class FooCollectionModel 
{
    public string FooMember { get; set; }
}

FooCollectionViewModel.cs

public class FooCollectionViewModel 
{
    public string FooMember { get; set; }
}

This is how the DI is registered:

services.AddAutoMapper(cfg =>
        {
            cfg.AddMaps(new[]
            {
                "FooNamespace.Web",
                "FooNamespace.Web.Framework",
            });
        });

This particular mapping profile is contained within FooNamespace.Web. There are no issues with any other maps in this profile.

AutoMapper throws an error that it cannot create an instance of FooCollectionResolver at runtime. I believe it attempts to create an instance via reflection when using the generic .MapFrom<T> method.

Exception:

AutoMapperMappingException: Cannot create an instance of type GPE.Web.Framework.Mapping.ValueResolvers.FooCollectionResolver`2[FooCollectionModel,FooCollectionViewModel]

AutoMapper.ResolutionContext.CreateInstance(Type type)

AutoMapperMappingException: Error mapping types.

Mapping types: FooModel -> FooViewModel

Type Map configuration: FooModel -> FooViewModel

Destination Member: BlockListModelMember

Does anyone know why this is?

I can get this working with an inline resolution function e.g:

this.CreateMap<FooModel, FooViewModel>()
                .ForMember(
                dest => dest.BlockListModelMember,
                src => src.MapFrom((src, dest, destMember, context) =>
                {
                    // mapping logic
                }));

But I want to reuse this logic as its the same general pattern applied to various different models, hence the generic IMemberValueResolver

CodePudding user response:

When you use that specific overload of services.AddAutoMapper it is not adding your value resolver to the container. You can use this instead which will scan the assemblies looking for all relevant types:

services.AddAutoMapper(
    typeof(FooCollectionResolver<,>).Assembly,
    typeof(SomeOtherType).Assembly /*, more if required */);

If you look at the implementation of AddAutoMapperClasses, which is what the AddAutoMapper extension method ultimately calls, you will see that it only registers IMemberValueResolver types (amoung others) if it's passed a collection of assembliesToScan.

The overload that you're using passes null for this parameter so they won't be registered.

  • Related