Search code examples
c#asp.netcqrsmediatr

System.AggregateException: Some services are not able to be constructed


I'm writing a project on ASP-NET Core using the MediatR 12.0.1 library. I'm new to .net 6 and I'm learn Dependency Injection. Structure

using Application;
using Application.Common;
using Domain;
using Domain.Interfaces;
using Microsoft.EntityFrameworkCore;
using System.Reflection;

namespace Tavrida.Backend
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            builder.Services.AddDbContext<AppDbContext>(opt =>
            {
                if (builder.Environment.IsDevelopment())
                    opt.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"));

                if (builder.Environment.IsProduction())
                {
                    var database = Environment.GetEnvironmentVariable("PGDATABASE");
                    var host = Environment.GetEnvironmentVariable("PGHOST");
                    var user = Environment.GetEnvironmentVariable("PGUSER");
                    var password = Environment.GetEnvironmentVariable("PGPASSWORD");
                    var port = Environment.GetEnvironmentVariable("PGPORT");
                    var connection = $"User ID={user};Password={password};Host={host};Port={port};Database={database}";
                    opt.UseNpgsql(connection);
                }
            });

            builder.Services.AddScoped<IForumContext>(provider => provider.GetService<AppDbContext>());
            builder.Services.AddScoped<IModelContext>(provider => provider.GetService<AppDbContext>());
            builder.Services.AddScoped<IUserContext>(provider => provider.GetService<AppDbContext>());

            using (var scope = builder.Services.BuildServiceProvider())
            {
                try
                {
                    var context = scope.GetRequiredService<AppDbContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception exception)
                {
                    var logger = scope.GetRequiredService<ILogger<Program>>();
                    logger.LogError(exception, "An error occurred while app initialization");
                }
            }
            builder.Services.AddApplicationModule();

            builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));

            builder.Services.AddControllers();
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            var app = builder.Build();

            if (app.Environment.IsDevelopment())
                app.UseDeveloperExceptionPage();

            app.UseSwagger();
            app.UseSwaggerUI();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            app.Run();
        }
    }
}

The build of this project is successful, but in runtime it calls System.AggregateException

System.AggregateException: "Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2[Application.Forums.Queries.GetForumList.GetForumListQuery,Application.Forums.Queries.GetForumList.ForumListVm] Lifetime: Transient ImplementationType: Application.Forums.Queries.GetForumList.GetForumListQueryHandler': Unable to resolve service for type 'MapsterMapper.IMapper' while attempting to activate 'Application.Forums.Queries.GetForumList.GetForumListQueryHandler'.) (Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2[Application.Forums.Queries.GetForumDetail.GetForumDetailQuery,Application.Forums.Queries.GetForumDetail.ForumDetailVm] Lifetime: Transient ImplementationType: Application.Forums.Queries.GetForumDetail.GetForumDetailQueryHandler': Unable to resolve service for type 'MapsterMapper.IMapper' while attempting to activate 'Application.Forums.Queries.GetForumDetail.GetForumDetailQueryHandler'.) (Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2[Tavrida.Backend.Auth.Users.Queries.AuthUser.LoginDefault.LoginDefaultQuery,System.IdentityModel.Tokens.Jwt.JwtSecurityToken] Lifetime: Transient ImplementationType: Tavrida.Backend.Auth.Users.Queries.AuthUser.LoginDefault.LoginDefaultQueryHandler': Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[Domain.Models.User]' while attempting to activate 'Tavrida.Backend.Auth.Users.Queries.AuthUser.LoginDefault.LoginDefaultQueryHandler'.)"

The code of one of my handlers

using Domain.Interfaces;
using Mapster;
using MapsterMapper;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Application.Forums.Queries.GetForumList
{
    public class GetForumListQueryHandler : IRequestHandler<GetForumListQuery, ForumListVm>
    {
        private readonly IForumContext _context;
        private readonly IMapper _mapper;

        public GetForumListQueryHandler(IForumContext context, IMapper mapper) =>
            (_context, _mapper) = (context, mapper);

        public async Task<ForumListVm> Handle(GetForumListQuery request, CancellationToken cancellationToken)
        {
            var forums = await _mapper.From(_context.Forums
                    .OrderBy(x => x.StartedAt)
                    .Skip(request.Skiped)
                    .Take(request.Count))
                .ProjectToType<ForumDto>()
                .ToListAsync(cancellationToken);

            return new ForumListVm { ForumList = forums };
        }
    }
}
using MediatR;

namespace Application.Forums.Queries.GetForumList
{
    public class GetForumListQuery : IRequest<ForumListVm>
    {
        public int Count { get; set; }
        public int Skiped { get; set; }
    }
}
namespace Application.Forums.Queries.GetForumList
{
    public class ForumListVm
    {
        public IList<ForumDto>? ForumList { get; set; }
    }
}
namespace Application.Forums.Queries.GetForumList
{
    public class ForumDto
    {
        public string? Id { get; set; }
        public string? Title { get; set; }
        public string? LogoUrl { get; set; }
    }
}

To register the service, I use the class

using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace Application
{
    public static class ApplicationModule
    {
        public static IServiceCollection AddApplicationModule(this IServiceCollection services)
        {

            services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));

            return services;
        }
    }
}

I add the MediatoR library to the controller with the following code

        private IMediator _mediator;
        protected IMediator Mediator =>
            _mediator ??= HttpContext.RequestServices.GetService<IMediator>();

I have tried different MediatR registration methods. All my attempts end with two scenarios:

  1. Handler was not found for request of type MediatR.IRequestHandler
  2. Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler

I read a similar article, but I didn't understand how to solve my problem. Article I would be very grateful for any help!


Solution

  • You have some missing registrations in your dependency injection setup.

    The aggregate exception thrown shows 3 inner exceptions.

    2 of them indicate that there is no IMapper registered (it appears you are using Mapster).

    Add this above your AddDbContext call in Program.cs (see here) for more info):

    builder.Services.AddMapster()
    

    This should take care of the first two errors in your aggregate exception.

    The 3rd exception indicates that there is no UserManager<Domain.Models.User> registered. Your class, Tavrida.Backend.Auth.Users.Queries.AuthUser.LoginDefault.LoginDefaultQueryHandler evidently has a constructor parameter of type UserManager<Domain.Models.User>, but your host doesn't now how to create that object type.

    Considering that you are using your own custom-implemented user model (Domain.Models.User), make sure this class implements the built in ASP.NET Core IdentityUser class

    using System;
    using Microsoft.AspNetCore.Identity;
    
    public class User : IdentityUser<Guid>
    {
       // your custom properties here
    }
    

    Then in your Program.cs, the following should register ASP.Net Core identity-related classes in DI for you, such as UserManager.

    builder.services.AddDefaultIdentity<Domain.Models.User>()
    app.UseAuthentication();
    app.UseAuthorization();
    

    I suggest reviewing the ASP.Net Core Identity documentation for the recommended way to implement identity using built in functionality.