Search code examples
c#asp.netapidependency-injection

ASP.NET Core - AggregateException: some services are not able to be constructed


I'm pretty sure it's an error with DI, but I don't know how to fix it.

using Microsoft.EntityFrameworkCore;
using RestApi.Models;

namespace RestApi.Services;

public class Builder
{
    public static WebApplication Configurate(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        var connectionString = builder.Configuration.GetConnectionString("AppDb");

        // Add services to the container.
        builder.Services.AddControllers();
        
        // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
        builder.Services.AddEndpointsApiExplorer();
        builder.Services.AddSwaggerGen();
        
        // User defined
        builder.Services.AddScoped<IUserService, UserService>();
        builder.Services.AddScoped<IPassword, Password>();

        builder.Services.AddDbContext<ApiContext>(x => x.UseSqlServer(connectionString));

        return builder.Build();
    }
}
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;

namespace RestApi.Services;

public class Password : IPassword
{
    public Password()
    {
    }
    
    public string Hash(string password, byte[]? userSalt = null)
    {
        // Generate a 128-bit salt using a sequence of
        // cryptographically strong random bytes.
        byte[]? salt;
        
        salt = userSalt ?? RandomNumberGenerator.GetBytes(128 / 8); // divide by 8 to convert bits to bytes

        // derive a 256-bit subkey (use HMACSHA256 with 100,000 iterations)
        var hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
            password: password!,
            salt: salt,
            prf: KeyDerivationPrf.HMACSHA256,
            iterationCount: 100000,
            numBytesRequested: 256 / 8));
        
        return hashed;
    }

    public bool Validate(string password, byte[] salt, string correctPassword)
    {
        var hashed = Hash(password, salt);

        if (hashed == correctPassword)
        {
            return true;
        }

        return false;
    }
}
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RestApi.Models;

namespace RestApi.Services;

public class UserService : IUserService
{
    private readonly ApiContext _context;
    private readonly Password _password;

    public UserService(ApiContext context, Password password)
    {
        _context = context;
        _password = password;

        if (_context.Users == null)
            throw new Exception();
    }
    
    public async Task<IEnumerable<UserModel>> All()
    {
        return await _context.Users.ToListAsync();
    }

    public async Task<ActionResult<UserModel>> Create(UserModel userModel)
    {
        userModel.Password = _password.Hash(userModel.Password);
        _context.Users.Add(userModel);
        await _context.SaveChangesAsync();

        return new UserModel
        {
            Id = userModel.Id
        };
    }

    public async Task<ActionResult<UserModel>> GetById(long id)
    {
        var userModel = await _context.Users.FindAsync(id);

        if (userModel == null)
            return null;

        return userModel;
    }

    public async Task<bool> Delete(long id)
    {
        var userModel = await _context.Users.FindAsync(id);
        if (userModel == null)
            return false;

        _context.Users.Remove(userModel);
        await _context.SaveChangesAsync();

        return true;
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RestApi.Models;
using RestApi.Services;

namespace RestApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UsersController : ControllerBase
    {
        private readonly IUserService _userService;

        public UsersController(IUserService userService)
        {
            _userService = userService;
        }

        // GET: api/Users
        [HttpGet]
        public async Task<IEnumerable<UserModel>> GetUsers()
        {
            return await _userService.All();
        }
        
        // POST: api/Users
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPost]
        public async Task<ActionResult<UserModel>> PostUserModel(UserModel userModel)
        {
            return CreatedAtAction("GetUserModel", await _userService.Create(userModel), userModel);
        }

        // GET: api/Users/5
        [HttpGet("{id}")]
        public async Task<ActionResult<UserModel>> GetUserModel(long id)
        {
            ActionResult<UserModel> user = await _userService.GetById(id);
            
            if (user == null)
            {
                return NotFound();
            }

            return user;
        }
        
        // DELETE: api/Users/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteUserModel(long id)
        {
            if (!await _userService.Delete(id))
                return NotFound();
            return Ok();
        }

        /*// PUT: api/Users/5
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPut("{id}")]
        public async Task<IActionResult> PutUserModel(long id, UserModel userModel)
        {
            if (id != userModel.Id)
            {
                return BadRequest();
            }

            _context.Entry(userModel).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!UserModelExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        private bool UserModelExists(long id)
        {
            return (_context.Users?.Any(e => e.Id == id)).GetValueOrDefault();
        }*/
    }
}

An error occurred while starting the application:

AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: RestApi.Services.IUserService Lifetime: Scoped ImplementationType: RestApi.Services.UserService': Unable to resolve service for type 'RestApi.Services.Password' while attempting to activate 'RestApi.Services.UserService'.)

Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection serviceDescriptors, ServiceProviderOptions options)

InvalidOperationException: Error while validating the service descriptor 'ServiceType: RestApi.Services.IUserService Lifetime: Scoped ImplementationType: RestApi.Services.UserService': Unable to resolve service for type 'RestApi.Services.Password' while attempting to activate 'RestApi.Services.UserService'.

Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(ServiceDescriptor descriptor)

AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: RestApi.Services.IUserService Lifetime: Scoped ImplementationType: RestApi.Services.UserService': Unable to resolve service for type 'RestApi.Services.Password' while attempting to activate 'RestApi.Services.UserService'.)

I am trying to inject the dependencies and start the program


Solution

  • You injected the IPassword interface but used the Password class in UserService instead of the interface.

    change your UserService class to this

     public UserService(ApiContext context, IPassword password)
        {
            _context = context;
            _password = password;
    
            if (_context.Users == null)
                throw new Exception();
        }