Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No parameterless constructor defined for this object in automapper

Question

I have the following value resolver:

public class StudentResolver : IValueResolver<Lesson, LessonResponse, UserResponse>
{
    private readonly ApplicationDbContext _dbContext;

    public StudentResolver(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;
    }
    
    public UserResponse Resolve(
        Lesson lesson, 
        LessonResponse response, 
        UserResponse member, 
        ResolutionContext context)
    {
        var user = _dbContext.Users
            .Where(x => x.Id == lesson.StudentId)
            .FirstOrDefault();
        if (user == null)
            return null;
        var result = Mapper.Map<UserResponse>(user); // this line triggers a "no parameterless constructor", why?
        return result;
    }
}

This resolver tries to fetch the Student attribute for this model:

public class LessonResponse
{
    public string Id { get; set; }

    public UserResponse Student { get; set; }
}

But on the line:

var result = Mapper.Map<UserResponse>(user);

I get:

{System.MissingMethodException: No parameterless constructor defined for this object. at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor) at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) at System.Activator.CreateInstance(Type type, Boolean nonPublic) at System.Activator.CreateInstance(Type type) at AutoMapper.MappingOperationOptions`2.CreateInstanceT}

The only type in that expression that should be created is UserResponse which is defined as follows:

public class UserResponse
{
    public string Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string FullName { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public string PhoneNumber { get; set; }
    public string Picture { get; set; }
    public string X { get; set; }

    public bool IsAdmin { get; set; }
    public bool IsClient { get; set; }
    public bool IsInstructor { get; set; }
}

But it is constructible, so what am I doing wrong?


Extra

The mapping profile is the following:

public class MappingProfile : Profile 
{
    public MappingProfile() {
        CreateMap<Lesson, LessonResponse>()                
            .ForMember(
                dest => dest.Student,
                opts => opts.ResolveUsing<StudentResolver>());

    }
}

And the mapping for the user is:

public class MappingProfile : Profile 
{
    public MappingProfile() {
        CreateMap<ApplicationUser, UserResponse>()
            .ForMember(
                dest => dest.FullName, 
                opts => opts.MapFrom(
                    origin => origin.FirstName + " " + origin.LastName))
            .ForMember(
                dest => dest.IsAdmin,
                opts => opts.ResolveUsing<IsAdminResolver>())
            .ForMember(
                dest => dest.IsInstructor,
                opts => opts.ResolveUsing<IsInstructorResolver>())
            .ForMember(
                dest => dest.IsClient,
                opts => opts.ResolveUsing<IsClientResolver>());
    }
}

The boolean resolvers are all similar to:

public class IsAdminResolver : IValueResolver<ApplicationUser, UserResponse, bool>
{
    private readonly UserManager<ApplicationUser> _userManager;

    public IsAdminResolver(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }
    
    public bool Resolve(ApplicationUser user, UserResponse response, bool member, ResolutionContext context)
    {
        return _userManager.IsInRoleAsync(user, "Admin").Result;
    }
}
like image 508
Shoe Avatar asked Dec 02 '22 12:12

Shoe


2 Answers

I had the same problem because of my inattention. I generated it with Visual Studio quick tip default constructor for the class inherited from Profile which was protected instead of public. I hope this will help somebody.

like image 53
kkost Avatar answered Dec 27 '22 07:12

kkost


sorry for long response time since yesterday was exhausted after work.

I was able to replicate your issue. You have 2 options:

1st Option Resolve your StudentResolver with your DbContext:

CreateMap<Lesson, LessonResponse>()
            .ForMember(
                dest => dest.Student,
                opts => opts.ResolveUsing(new StudentResolver(ApplicationDbContext.Create())));

2nd Option, configure your mapper configuration ConstructServicesUsing. In my case I was using Simple Injector to test things out.

Mapper.Initialize(cfg =>
        {
            cfg.ConstructServicesUsing(type => container.GetInstance(typeof(StudentResolver)));
            cfg.AddProfile<MappingProfile>();
        });

For my testing, I was getting an error thrown var lessonResponse = Mapper.Map<LessonResponse>(lesson);

So if your is thrown for Mapper.Map<UserResponse>(user); you may have to ConstructService for IsAdminResolver, IsInstructorResolver and IsClientResolver

like image 20
penleychan Avatar answered Dec 27 '22 05:12

penleychan