Search code examples
c#.net-coreasp.net-web-apiclean-architecturemediatr

Asp.WebAPI with Mediatr12 and FluentValidation (migration)


I'm creating a Asp.Net Web Api, using clean architecture, CQRS, MediatR and FluentValidation. Recently, MediatR was updated to version 12. I'm having troubles doing the migration (from v11 to v12).

Everything works fine using MediatR v11. But while using v12, the *RequestHandlers *are not automatically detected at run-time.

The exception:

No service for type 'MediatR.IRequestHandler`2[Application.UseCases.Weather.Queries.GetWeatherQuery,CSharpFunctionalExtensions.Result`1[System.Collections.Generic.List`1[Application.UseCases.Weather.Queries.GetWeatherResponse]]]' has been registered.

I created a small repository, using MediatR v11 and v12. Can you help me?

I'm using clean architecture, with the usual layers:

  • Api layer (app's main entry point)
  • Application (commands,queries and query handlers)
  • Domain (entities)

MediatR v12 project dependencies:

Api project:

<ItemGroup>
    <PackageReference Include="FluentValidation" Version="11.5.1" />
    <PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
    <PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.5.1" />
    
    <PackageReference Include="MediatR" Version="12.0.1" />
    <PackageReference Include="MediatR.Extensions.FluentValidation.AspNetCore" Version="4.0.0" />
    
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.3" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

Application project:

<ItemGroup>
    <PackageReference Include="CSharpFunctionalExtensions" Version="2.38.1" />
    <PackageReference Include="FluentValidation" Version="11.5.1" />
    <PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>

MediatR v11 project dependencies:

Api project:

<ItemGroup>
    <PackageReference Include="FluentValidation" Version="11.5.1" />
    <PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
    <PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.5.1" />
    
    <PackageReference Include="MediatR" Version="11.1.0" />
    <PackageReference Include="MediatR.Extensions.FluentValidation.AspNetCore" Version="4.0.0" />
    <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.1.0" />
    
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.3" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

Application project:

<ItemGroup>
    <PackageReference Include="CSharpFunctionalExtensions" Version="2.38.1" />
    <PackageReference Include="FluentValidation" Version="11.5.1" />
    <PackageReference Include="MediatR" Version="11.1.0" />
</ItemGroup>

I'm using the sample code for WeatherForecastController.

I follow the migration guide


Solution

  • That GetWeatherQuery and its corresponding GetWeatherQueryHandler are in your Application assembly.

    In your MediatR v11 version you register that handler in a single call passing in all loaded assemblies, which includes that Application assembly.

    var assemblies = AppDomain.CurrentDomain.GetAssemblies();
    services.AddMediatR(assemblies);
    

    In your MediatR v12 version based code you only register the handlers present in the Api assembly.

    services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
    

    To also register the ones in the Application assembly in a similar way you need to call RegisterServicesFromAssemblies passing in the same set of assemblies.

    var assemblies = AppDomain.CurrentDomain.GetAssemblies();
    services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(assemblies));
    

    Alternatively, you can be more explicit and only call RegisterServicesFromAssembly on the 2 concerning assemblies containing MediatR handlers and so.
    This has the benefit of less assembly scanning, since above AppDomain.CurrentDomain.GetAssemblies() call returns quite some assemblies.

    services.AddMediatR(cfg => 
    {
        cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
        cfg.RegisterServicesFromAssembly(typeof(GetWeatherQuery).Assembly);
    });