0

In my ASP.NET Core Web API, I have IdentityDbContext:

public class MyDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, long, IdentityUserClaim<long>, ApplicationUserRole, IdentityUserLogin<long>, IdentityRoleClaim<long>, IdentityUserToken<long>>
{
    public MyDbContext(DbContextOptions<MyDbContext> options)
        : base(options)
    {
    }

    public DbSet<ApplicationRole> ApplicationRole { get; set; }
    public DbSet<ApplicationUserRole> ApplicationUserRole { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<ApplicationUser>(entity =>
        {
            entity.Property(u => u.Id).ValueGeneratedOnAdd();
            entity.HasIndex(u => u.Email).IsUnique();
            entity.HasIndex(u => u.UserName).IsUnique();
        });
        builder.Entity<ApplicationRole>(entity =>
        {
            entity.Property(r => r.Id).ValueGeneratedOnAdd();
            entity.HasIndex(r => r.Name).IsUnique();
        });
}

IdentityModel:

 public class ApplicationUser : IdentityUser<long>
 {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string MobileNumber { get; set; }

    [JsonIgnore]
    public bool? IsDeleted { get; set; }
    public DateTime? LastLogin { get; set; }
    public ICollection<ApplicationUserRole> UserRoles { get; set; }
 }

 public class ApplicationRole : IdentityRole<long>
 {
    public ICollection<ApplicationUserRole> UserRoles { get; set; }
 }

 public class ApplicationUserRole : IdentityUserRole<long>
 {
    public override long UserId { get; set; }
    public override long RoleId { get; set; }
    public virtual ApplicationUser User { get; set; }
    public virtual ApplicationRole Role { get; set; }
 }
} 

LoginRequestDto:

public class LoginRequestDto
{
    [Required]
    [JsonProperty(PropertyName = "username")]
    public string UserName { get; set; }

    [Required(ErrorMessage = "The password is required!")]
    public string Password { get; set; }
}

UserDto:

public class UserDto
{
    public long Id { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public string Email { get; set; }
    public string UserName { get; set; }
    public string MobileNumber { get; set; }
    public DateTime LastLogin { get; set; }
}

AuthMapper:

public class AuthMapperProfile : Profile
{
    public AuthMapperProfile()
    {
        CreateMap<ApplicationUser, UserDto>();

}

Services:

public interface IAuthService
{
    Task<GenericResponseDto<object>> LoginUser(LoginRequestDto request);
}

public class AuthService : IAuthService
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<ApplicationRole> _roleManager;
    private readonly IConfiguration _configuration;
    private readonly IMapper _mapper;
    private readonly MyDbContext _context;

    public AuthService(
        UserManager<ApplicationUser> userManager,
        RoleManager<ApplicationRole> roleManager,
        IConfiguration configuration,
        IMapper mapper,
        MyDbContext context

    )
    {
        _userManager = userManager;
        _roleManager = roleManager;
        _configuration = configuration;
        _mapper = mapper;
        _context = context;
    }

    public async Task<GenericResponseDto<object>> LoginUser(LoginRequestDto request)
    {
        var user = await _userManager.FindByNameAsync(request.UserName);
        var response = new GenericResponseDto<object>();

        if (user != null && await _userManager.CheckPasswordAsync(user, request.Password))
        {
            var roles = await _userManager.GetRolesAsync(user);
            var authClaims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            };
            foreach (var userRole in roles)
            {
                authClaims.Add(new Claim(ClaimTypes.Role, userRole));
            }
            var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
            var token = new JwtSecurityToken(
                issuer: _configuration["JWT:ValidIssuer"],
                audience: _configuration["JWT:ValidAudience"],
                expires: DateTime.Now.AddHours(3),
                claims: authClaims,
                signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
            );

            response.Result = new
            {
                token = new JwtSecurityTokenHandler().WriteToken(token),
                user = _mapper.Map<UserDto>(user),
                expires = token.ValidTo
            };
            response.StatusCode = 200;
            user.LastLogin = DateTime.Now;
            try
            {
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                response.Error = new ErrorResponseDto() { ErrorCode = 500, Message = ex.Message };
            }
            return response;
        }

        response.StatusCode = 400;
        response.Error = new ErrorResponseDto { ErrorCode = 400, Message = "Invalid Username or Password!" };

        return response;
    }
}

I got this error:

AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.

Mapping types: Object -> UserDto System.Object -> DDM.API.Core.DTOs.v1.Authentication.UserDto at lambda_method198(Closure , Object , UserDto , ResolutionContext ) at DDM.API.Core.Services.v1.Concrete.AuthService.LoginUser(LoginRequestDto request) in C:\Users\MyApp\Core\Services\v1\Concrete\AuthService.cs:line 78

This is line 78:

response.Result = new

startup.cs:

  // Auto mapper
  services.AddAutoMapper(typeof(Startup));

  // Dependency Injection
  services.AddScoped<IAuthService, AuthService>();

controller:

    [Produces("application/json")]
    [HttpPost("login")]
    public async Task<ActionResult<GenericResponseDto<object>>> Login(LoginRequestDto loginRequest)
    {
        var response = await _authService.LoginUser(loginRequest);
        Response.StatusCode = response.StatusCode ?? StatusCodes.Status200OK;
        return new JsonResult(response);
    }

What do I do to resolve this?

Thanks

5
  • What does your mapper registration as a service look like? Can you add code please? Commented Dec 17, 2021 at 16:31
  • @MartinStaufcik - See AuthMapper: and startup.cs in my code. Thanks Commented Dec 18, 2021 at 4:36
  • Can you verify that the type of user is ApplicationUser? Commented Dec 18, 2021 at 4:55
  • @MattU - Yes, it is. See it in MyDbContext Commented Dec 18, 2021 at 5:46
  • 1
    It looks like you have multiple projects. can your try services.AddAutoMapper(typeof(Startup), typeofof(AuthService)); Also, try moving the assignment out of the object creation Commented Dec 18, 2021 at 8:19

1 Answer 1

3

For some reason, your mapping Profile is not taken into account, the error message supports this, the error message says Automapper uses mapping of Object to UserDto, instead of ApplicationUser to UserDto.

Try configure the mapper in an alternate way

var config = new MapperConfiguration(cfg => {
    cfg.AddMaps(myAssembly);
});

IMapper mapper = config.CreateMapper();

services.AddSingleton(mapper);

Another option, try mapping manually

var config = new MapperConfiguration(cfg => {
    cfg.CreateMap<ApplicationUser, UserDto>();
});
Sign up to request clarification or add additional context in comments.

3 Comments

Basically I have three (3) projects: 1. DDM.API.Infrasteucture (It contains the DB Migration and entities. It is referenced by the DDM.API.Core). 2. DDM.API.Core (It consist the DTOs and Services. This is where the mapping is done. It is referenced by DDM.API.Web). 3. DDM.API.Web ( It consist of startup.cs)
The documentation for injection states you define the configuration using profiles. And then you let AutoMapper know in what assemblies are those profiles defined by calling the AddAutoMapper method. You need to provide the profile type as a parameter: services.AddAutoMapper(typeof(AuthMapperProfile));.
@mike as Martin pointed out, when you AddAutoMapper you need to tell it where any and all Profiles live. If AuthMapperProfile lives in a separate assembly from Startup then that's a problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.