I am having a Project on ASP.NET Core-6 Web API using MapsterMapper, Fluent Validation and IMediatR

Interfaces:

public interface IRequestWrapper<T> : IRequest<ServiceResult<T>>
{

}

public interface IRequestHandlerWrapper<in TRequest, TResponse> : IRequestHandler<TRequest, ServiceResult<TResponse>>
    where TRequest : IRequestWrapper<TResponse>
{
}

I did the response with ServiceResult as shown below.

ServiceResult:

public class ServiceResult<T> : ServiceResult
{
    public T Data { get; set; }

    public ServiceResult(T data)
    {
        Data = data;
    }

    public ServiceResult(T data, ServiceError error) : base(error)
    {
        Data = data;
    }

    public ServiceResult(ServiceError error) : base(error)
    {

    }
}

public class ServiceResult
{
    public bool Successful => this.Error == null;

    public ServiceError Error { get; set; }

    public ServiceResult(ServiceError error)
    {
        if (error == null)
        {
            error = ServiceError.DefaultError;
        }

        Error = error;
    }

    public ServiceResult() { }

    public static ServiceResult Failed(ServiceError error)
    {
        return new ServiceResult(error);
    }

    public static ServiceResult<T> Failed<T>(ServiceError error)
    {
        return new ServiceResult<T>(error);
    }

    public static ServiceResult<T> Failed<T>(T data, ServiceError error)
    {
        return new ServiceResult<T>(data, error);
    }

    public static ServiceResult<T> Success<T>(T data)
    {
        return new ServiceResult<T>(data);
    }
}

ApplicationUserDto:

public class ApplicationUserDto : IRequest
{
    public String Id { get; set; }
    public string Email { get; set; }
    public string UserName { get; set; }
    public string MobileNumber { get; set; }
    public bool? IsAdmin { get; set; }
    public DateTime LastLogin { get; set; }

    public void Register(TypeAdapterConfig config)
    {
        config.NewConfig<ApplicationUser, ApplicationUserDto>();
    }
}

AuthResult:

public class AuthResult
{
    public string AccessToken { get; set; }
    public string TokenType { get; set; }
    public int ExpiresIn { get; set; }
    public string RefreshToken { get; set; }
    public ApplicationUserDto User { get; set; }
    public IList<string> Roles { get; set; }
}

JwtTokenManager:

using MapsterMapper;

public class JwtTokenManager : IJwtTokenManager
{
    private readonly JwtSettings _jwtSettings;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly TokenValidationParameters _tokenValidationParameters;
    private readonly IMapper _mapper;

    public JwtTokenManager(
        JwtSettings jwtSettings, 
        UserManager<ApplicationUser> userManager, 
        TokenValidationParameters tokenValidationParameters,
        IMapper mapper
        )
    {
        _jwtSettings = jwtSettings;
        _userManager = userManager;
        _tokenValidationParameters = tokenValidationParameters;
        _mapper = mapper;
    }

    public async Task<AuthResult> GenerateClaimsTokenAsync(string username, CancellationToken cancellationToken)
    {
        var user = await _userManager.FindByNameAsync(username);
        var roles = await _userManager.GetRolesAsync(user);

        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_jwtSettings.Secret);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {

                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // TODO: encrypt user id for added security
                new Claim(ClaimTypes.Name, username),
                new Claim(JwtRegisteredClaimNames.Sub, username),
                new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
                new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddMinutes(5)).ToUnixTimeSeconds().ToString()),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),

            }),
            Expires = DateTime.UtcNow.Add(_jwtSettings.Expiration),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };

        var refreshTokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // TODO: encrypt user id for added security
                new Claim(ClaimTypes.Name, username),
                new Claim(JwtRegisteredClaimNames.Iss, _jwtSettings.Issuer),
                new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
                new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
                new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddMinutes(30)).ToUnixTimeSeconds().ToString()),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),

            }),
            Expires = DateTime.UtcNow.Add(_jwtSettings.Expiration),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };

        // Create JWT tokens
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var refreshtoken = tokenHandler.CreateToken(refreshTokenDescriptor);

        return new AuthResult
        {
            AccessToken = tokenHandler.WriteToken(token),
            TokenType = "Bearer",
            ExpiresIn = _jwtSettings.Expiration.Seconds,
            RefreshToken = tokenHandler.WriteToken(refreshtoken),
            User = _mapper.Map<ApplicationUserDto>(user),
            Roles = roles
        };
    }
}

SignInCommand:

public class SignInCommand : IRequestWrapper<AuthResult>
{
    public string Username { get; set; }
    public string Password { get; set; }
}

the return AuthResult in JwtTokenManager is passed to SignInCommandHandler. Also SignInCommand is the request while AuthResult is the response.

SignInCommandHandler:

public class SignInCommandHandler : IRequestHandlerWrapper<SignInCommand, AuthResult>
{
    private readonly ISignInManager _signInManager;
    private readonly IJwtTokenManager _jwtTokenManager;
    private readonly IIdentityService _identityService;

    public SignInCommandHandler(ISignInManager signInManager, IJwtTokenManager jwtTokenManager, IIdentityService identityService)
    {
        _signInManager = signInManager;
        _jwtTokenManager = jwtTokenManager;
        _identityService = identityService;
    }
    public async Task<ServiceResult<AuthResult>> Handle(SignInCommand request, CancellationToken cancellationToken)
    {
        // validate username & password 
        var result = await _signInManager.PasswordSignInAsync(request.Username.ToLower().Trim(), request.Password.Trim(), false, false);

        // Throw exception if credential validation failed
        if (!result.Successful)
        {
            throw new UnauthorizedException("Invalid username or password.");
        }

        // Generate JWT token response if validation successful
        return ServiceResult.Success(await _jwtTokenManager.GenerateClaimsTokenAsync(request.Username, cancellationToken));
    }
}

Now the Controller:

public class AuthController : BaseApiController
{
    private readonly IMediator _mediator;
    public AuthController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpPost]
    [Route(ApiRoutes.Auth.SignIn)]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public async Task<ActionResult<ServiceResult<AuthResult>>> SignIn(SignInRequest request, CancellationToken cancellationToken)
    {
        return Ok(await _mediator.Send(request, cancellationToken));
    }
}

DependencyInjection:

public static class DependencyInjection
{
    public static IServiceCollection AddApplication(this IServiceCollection services)
    {
        // Register OAuth services
        services.AddTransient<IJwtTokenManager, JwtTokenManager>();
        services.AddScoped<ISignInManager, SignInManager>();  
        services.AddTransient<IIdentityService, IdentityService>();
        // Register Application Health Checks
        services.AddHealthChecks()
            .AddCheck<ApplicationHealthCheck>(name: "MY API");

        // Register Fluent Validation service
        services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());

        // Register MediatR Services
        services.AddMediatR(Assembly.GetExecutingAssembly());
        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>));
        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>));
        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(PerformanceBehaviour<,>));

        return services;
    }

    private static TypeAdapterConfig GetConfiguredMappingConfig()
    {
        var config = TypeAdapterConfig.GlobalSettings;
        IList<IRegister> registers = config.Scan(Assembly.GetExecutingAssembly());
         config.Apply(registers);

        return config;
    }
}

Program.cs:

var builder = WebApplication.CreateBuilder(args);
// Add library project references for DependencyInjection
builder.Services.AddApplication();

When I tried to Run the Application, I got this error:

System.AggregateException
  HResult=0x80131500
  Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2[Auth.Commands.SignIn.SignInCommand,Models.ServiceResult`1[Models.AuthResult]] Lifetime: Transient ImplementationType: Auth.Commands.SignIn.SignInCommandHandler': Unable to resolve service for type 'MapsterMapper.IMapper' while attempting to activate 'Oauth.JwtTokenManager'.) 
(Error while validating the service descriptor 'ServiceType: Interfaces.IJwtTokenManager Lifetime: Transient ImplementationType: Oauth.JwtTokenManager': Unable to resolve service for type 'MapsterMapper.IMapper' while attempting to activate 'Oauth.JwtTokenManager'.)
  Source=Microsoft.Extensions.DependencyInjection
  StackTrace:
   at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options)
   at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
   at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
   at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
   at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
   at Microsoft.Extensions.Hosting.HostBuilder.Build()
   at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build()
   at Program.<Main>$(String[] args) in C:\Users\HP\Desktop\MyApp\WebApi\Program.cs:line 183

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2[Auth.Commands.SignIn.SignInCommand,Models.ServiceResult`1[Models.AuthResult]] Lifetime: Transient ImplementationType: Auth.Commands.SignIn.SignInCommandHandler': Unable to resolve service for type 'MapsterMapper.IMapper' while attempting to activate 'Oauth.JwtTokenManager'.

Inner Exception 2:
InvalidOperationException: Unable to resolve service for type 'MapsterMapper.IMapper' while attempting to activate 'Oauth.JwtTokenManager'.

I already Mapped ApplicationUserDto

How do I resolve this

0

There are 0 answers