0

I've created an Application Assembly, which I referenced to an REST-Api and Blazor App. The REST-Api works fine but the Blazor app gives the error below:

InvalidOperationException: Cannot resolve 'MediatR.IRequestHandler`2[Application.Customers.Queries.GetCustomersList.GetCustomersListQuery,Application.Common.Viewmodels.CustomerListVm]' from root provider because it requires scoped service 'Application.Common.Interfaces.IWegisterDbContext'. Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)

InvalidOperationException: Error constructing handler for request of type MediatR.IRequestHandler`2[Application.Customers.Queries.GetCustomersList.GetCustomersListQuery,Application.Common.Viewmodels.CustomerListVm]. Register your handlers with the container. See the samples in GitHub for examples. MediatR.Internal.RequestHandlerBase.GetHandler(ServiceFactory factory)

In the code examples below I put my Starup files, both projects are in the same solution, referencing to the same projects. I already tried to add the Authorization, but that didn't work obviously. I have no clue what the problem is, am I missing something or is this Blazor? MediatR version in both assemblies are the same.

This is my startup from the REST-Api:

using Application;
using Application.Common.Interfaces;
using Infrastructure;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Persistence;
using WebApi.Services;

namespace Wegister.WebApi
{
    public class Startup
    {
        public IWebHostEnvironment Environment { get; }
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration, IWebHostEnvironment environment)
        {
            Configuration = configuration;
            Environment = environment;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddPersistence(Configuration);
            services.AddInfrastructure();
            services.AddApplication();

            if (Environment.IsDevelopment())
            {
                services.AddScoped<ICurrentUserService, CurrentUserServiceDev>();
            }

            services.AddAuthorization(x =>
            {
                if (Environment.IsDevelopment())
                    x.DefaultPolicy = new AuthorizationPolicyBuilder()
                    .RequireAssertion(_ => true)
                    .Build();
            });

            services.AddControllers().AddNewtonsoftJson(options =>
                options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
            );
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();
            app.UseHsts();

            app.UseRouting();

            app.UseAuthorization();

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

This is my Startup in the Blazor application:

using Application;
using Application.Common.Interfaces;
using Infrastructure;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Persistence;
using WebUI.Services;

namespace WebUI
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<SessionService>();
            services.AddSingleton<WorkHourService>();
            services.AddSingleton<ItemService>();
            services.AddSingleton<CustomerService>();

            services.AddPersistence(Configuration);
            services.AddInfrastructure();
            services.AddApplication();

            services.AddRazorPages();
            services.AddServerSideBlazor();

            services.AddScoped<ICurrentUserService, CurrentUserServiceDev>();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            });
        }
    }
}

The AddApplication Dependency Injection method is as following:

public static IServiceCollection AddApplication(this IServiceCollection services)
        {
            services.AddScoped<IRequestHandler<GetCustomersListQuery, CustomerListVm>, GetCustomersListQueryHandler>();
            services.AddMediatR(Assembly.GetExecutingAssembly());

            services.AddTransient<ICustomerFactory, CustomerFactory>();

            return services;
        }

AddPersistence:

public static IServiceCollection AddPersistence(this IServiceCollection services, IConfiguration configuration)
        {
            services.AddDbContext<WegisterDbContext>(options =>
                options.UseSqlServer(configuration.GetConnectionString("WegisterDbConnectionString")));

            services.AddScoped<IWegisterDbContext>(provider => provider.GetService<WegisterDbContext>());

            return services;
        }

Infra:

public static IServiceCollection AddInfrastructure(this IServiceCollection services)
        {
            services.AddTransient<IDateTime, MachineDateTime>();
            services.AddTransient<INotificationService, NotificationService>();

            return services;
        }
SaloméCodes
  • 57
  • 1
  • 9
  • 1
    Try to remove `services.AddScoped, GetCustomersListQueryHandler>();` Adding `services.AddMediatR(Assembly.GetExecutingAssembly())` is sufficient. – Max Aug 08 '21 at 14:20

1 Answers1

5

Unfortunately, the "Register your handlers" can have many different causes (as I have found myself). You will want to read this SO article on the matter. I linked you directly to the one that was the final fix for me, when your DB context is in a different assembly. But that was only the issue AFTER I had fixed a couple others noted in the article.

To summarize:

  1. Tell the serviceprovider not to validate scopes in CreateHostBuilder. (Thanks Sarah Akbari)
Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    }).UseDefaultServiceProvider(options =>
    options.ValidateScopes = false); // needed for mediatr DI
  1. Make sure your repos are there:
    services.AddScoped<IRepo1, Repo1>();
  1. Add MediatR after adding your repos, and give it assemblies for each assembly you have handlers, like so:
    var assembly = AppDomain.CurrentDomain.Load("HandlersDomain");
    var assembly2 = AppDomain.CurrentDomain.Load("HandlersDomain2");
    services.AddMediatR(assembly, assembly);

Follow this recipe, and you really should be gtg.

Zach Baker
  • 99
  • 4