I have a simple project to try out Mediatr issue. When the concrete class of my handler in the SAME project of my API, it WORKS. But, when I take that handler class in to a different project (and API references that project ofc), it does NOT resolve the registry.
I'm getting this error:
Handler was not found for request of type MediatR.IRequestHandler`2[MyBiz.GetTokenModelRequest,MyBiz.TokenModel]. Register your handlers with the container. See the samples in GitHub for examples.
I have this structure on my project and also shown where it works and where it doesn't:
For more clarification here are the codes:
MyApi2 -> Startup.cs:
namespace MyApi2
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddMediatR();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
}
MyApi2 -> ValuesController:
namespace MyApi2.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly IMediator _mediator;
public ValuesController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
try
{
var rr = await _mediator.Send(new GetTokenModelRequest());
}
catch (Exception ex)
{
throw;
}
return new string[] { "value1", "value2" };
}
}
}
MyBiz -> GetTokenModelRequest
namespace MyBiz
{
public class GetTokenModelRequest : LoginModel, IRequest<TokenModel>
{
}
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
}
public class TokenModel
{
#region Properties
public Guid Id { get; set; }
public string Username { get; set; }
public string Token { get; set; }
public DateTime Expiration { get; set; }
#endregion
}
}
MyInftra -> TokenQueryHandler
namespace MyInfra
{
public class TokenQueryHandler : ITokenQueryHandler
{
public Task<TokenModel> Handle(GetTokenModelRequest request, CancellationToken cancellationToken)
{
return Task.FromResult(new TokenModel());
}
}
}
So, if I MOVE TokenQueryHandler
from MyInfra
to MyApi
it works but I should be able to put it a references Project, right?
MediatR provides you with other functionality as well. It supports notifications mechanism. It may be very useful if you use domain events in your architecture. All classes of your events must implement INotification marker interface.
Most of the time it's used like a glorified Service Locator, which is notoriously an anti-pattern. Actually, no, it's never used as a service locator. No one uses the IMediator interface to locate services. They use it to dispatch requests and notifications to handlers.
MediatR Requests are very simple request-response style messages, where a single request is synchronously handled by a single handler (synchronous from the request point of view, not C# internal async/await). Good use cases here would be returning something from a database or updating a database.
As of version 7.0.0 of the MediatR.Extensions.Microsoft.DependencyInjection package, the AppDomain
no longer gets automatically scanned for loaded assemblies containing MediatR base types to register, when calling the AddMediatR()
extension method.
In fact, the parameter-less overload of said function has been completely removed from the package, requiring users to pass in the assemblies (or types) to scan through instead.
This makes the registration of the MediatR base types (IRequestHandler
, INotificationHandler
, IRequestPreProcessor
and IRequestPostProcessor
) within each referenced assembly, explicitly at the user's control and discretion.
So if we have some MediatR base types in the imaginary assemblies Assembly1
and Assembly2
that we want to register with the MediatR container:
Instead of doing: services.AddMediatR();
You'll need to do: services.AddMediatR(typeof(Assembly1), typeof(Assembly2));
This makes my original answer (below) redundant for anyone using version 7 (and perhaps greater) of this package but I will keep it here for those using older versions.
Note: The following answer is only relevant for versions < 7.0.0 of the MediatR.Extensions.Microsoft.DependencyInjection package.
The call to the AddMediatR()
extension method in your startup.cs
file does many things to initialise MediatR:
IRequestHandler
, INotificationHandler
, IRequestPreProcessor
and IRequestPostProcessor
)With the points above in mind, it is important to understand how the .NET CLR loads referenced assemblies. There is a really interesting blog post by Rick Strahl that goes into the details, but I will summarise it here with a quote:
In a nutshell, referenced assemblies are not immediately loaded - they are loaded on the fly as needed. So regardless of whether you have an assembly reference in a top level project, or a dependent assembly assemblies typically load on an as needed basis, unless explicitly loaded by user code. The same is true of dependent assemblies.
Why is this important to know?
Well, in your MyApi2
project, you reference the MyInfra
project but you do not actually use it in any way. This means that the assembly will not get loaded by the CLR, and thus MediatR will fail to find it in the App Domain's currently loaded assemblies. As a result, your IRequestHandler
will not be registered (nor any other MediatR base types in that project).
The solution to this problem is to ensure that the assembly containing the types you wish to have registered with the MediatR container is loaded before the call to AddMediatR()
.
You could do either of the following:
MyInfra
project from your MyApi2
projectThe latter option is the most typical, as you will usually have some functionality that sits in your referenced assembly that you will want to invoke (as opposed to just having an assembly that contains types).
Whichever option you go for, ensure that you do it prior to adding MediatR. Otherwise you will run into the same issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With