I use MediatR and CQRS in an application. The following two statements exist in many modules and may be called concurrently in the application (It's a blazor application).
await Mediator.Send(new AddIdCommand { Id = id });
and
await Mediator.Send(new DeleteIdCommand { Id = id });
The following are the commands, which update the same text file.
public class AddIdCommand : IRequest
{
public int Id { get; set; }
public class AddIdCommandHandler : IRequestHandler<DeleteIdCommand>
{
public async Task<Unit> Handle(AddIdCommand request, CancellationToken cancellationToken)
{
// .... update the text file with the Id deleted
}
}
}
public class DeleteIdCommand : IRequest
{
public int Id { get; set; }
public class DeleteIdCommandHandler : IRequestHandler<DeleteIdCommand>
{
public async Task<Unit> Handle(DeleteIdCommand request, CancellationToken cancellationToken)
{
// .... update the text file with the Id added
}
}
}
....
protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService<IMediator>();
Will the Handle(...)
in the two commands be called sequentially always? (So no need to worry about multiple processes updating the same file issue.)
Well, only from the context of these two lines:
await Mediator.Send(new AddIdCommand { Id = id });
await Mediator.Send(new DeleteIdCommand { Id = id });
It will run sequentially.
You can see the code here:
https://github.com/jbogard/MediatR/blob/master/src/MediatR/Mediator.cs
var requestType = request.GetType();
var handler = (RequestHandlerWrapper<TResponse>)_requestHandlers.GetOrAdd(requestType,
t => Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, typeof(TResponse))));
return handler.Handle(request, cancellationToken, _serviceFactory);
It simply returns the task of your async Handle method. So if you await the subsequent handle methods, it's running synchronously(relatively to each other).
But you end your question with
So no need to worry about multiple processes updating the same file issue.
And I'm not sure about that. Even if you are awaiting both send methods, if there's two independent proceses calling these two methods, and all of them target same file, the order between them won't be guaranteed. The only synchronization Mediator.cs is doing for a multi-threaded application is that _requestHandlers is a ConcurrentDictionary.
So it guarantees that this line will never initialize multiple handlers for the same type, even when called in parallel:
var handler = (RequestHandlerWrapper<TResponse>)_requestHandlers.GetOrAdd(requestType,
t => Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, typeof(TResponse))));
The Handle calls are not synchronized.
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