Closed cirrusone closed 2 years ago
Is this a reasonable way to use Mediatr without any foreseeable issues?
App DI:
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// MediatR DI dryloc
var container = containerRegistry.GetContainer();
container.RegisterDelegate<ServiceFactory>(r => r.Resolve);
container.RegisterMany(new[] { typeof(IMediator).GetAssembly(), typeof(MainViewModelPing).GetAssembly() }, Registrator.Interfaces);
}
GenericRequest and GenericResponse:
public class MainViewModelPing : IRequest<PingResponse>
{
public PingRequest Request { get; set; }
}
public abstract class PingRequest
{
}
public class GenericRequest<T> : PingRequest
{
public string Message { get; set; }
public T Data { get; set; }
}
public abstract class PingResponse
{
}
public class GenericResponse<T> : PingResponse
{
public string Message { get; set; }
public T Data { get; set; }
}
Generic Handler attached to MainViewModel:
public class MainWindowViewModel : BindableBase, IRequestHandler<MainViewModelPing, PingResponse>
{
public Task<PingResponse> Handle(MainViewModelPing request, CancellationToken cancellationToken)
{
// Test types and message to filter
if(request.Request is GenericRequest<string> stringRequest)
{
if(stringRequest.Message.Equals("SomeVal"))
{
// Perform action here
}
}
else if (request.Request is GenericRequest<int> intRequest)
{
if(stringRequest.Message.Equals("SomeOtherVal"))
{
// Perform action here
}
}
// Example response
var genericResponse = new GenericResponse<int> { Message = "It Works", Data = 42 };
return Task.FromResult(genericResponse as PingResponse);
}
}
Example request:
private async void MediatrTest()
{
var genericRequest = new GenericRequest<string> { Message = "SomeVal", Data = "" };
var response = await _mediator.Send(new MainViewModelPing() { Request = genericRequest });
}
I would say, making it more generic isn't an ideal use case.
The idea is that you have 1 request that maps to 1 handler because this follows SRP. It becomes easier to understand what the code is trying to achieve.
I would say that if you just want T
to be stored in a database, handled the same way each time... I would probably just make it object Data {get;set;}
and do some kind of reflection in the handler to get the object to "fit" into the database.
There maybe a design issue with what you're doing which has made this pattern emerge.
The pipeline handlers show how to "pass through" these requests and sort of do something with them.
@cirrusone do you have this figured out. I am trying to do the same thing with asp.net core DI container, have not find the solution yet. Thanks
I actually changed to Microsoft.Toolkit.Mvvm
as the messenger suited my use-case better. It also allows me to register lots of message types without lots of interfaces. Since it targets .NET Standard 2.0, this means that it can be used anywhere from UWP apps, to Uno, Xamarin, Unity, ASP.NET, etc. Literally any framework supporting the .NET Standard 2.0 feature set. There are no platform specific dependencies at all. The whole package is entirely platform, runtime, and UI stack agnostic.
https://github.com/windows-toolkit/WindowsCommunityToolkit/issues/3428
https://github.com/windows-toolkit/WindowsCommunityToolkit/issues/3230
https://github.com/windows-toolkit/MVVM-Samples/blob/master/docs/mvvm/Messenger.md
@cirrusone Thanks a lot, I will check it out also.
I have a similar issue.. I was able to resolve it by using AutoFac container. But wished I could have solved it using the default container.
Here is what I did...
Request...
public class AddNoteCommand<TEntity> : IRequest
where TEntity : class, INoteEntity
{
public int ParentId { get; set; }
public int CurrentUserProfileId { get; set; }
public string NoteContent { get; set; }
}
Handler
public class AddNoteCommandHandler<TEntity> : IRequestHandler<AddNoteCommand<TEntity>>
where TEntity : class, INoteEntity, new()
{
private readonly INoteRepository<TEntity> _repo;
private readonly ITime _time;
public AddNoteCommandHandler(INoteRepository<TEntity> repo, ITime time)
{
_repo = repo;
_time = time;
}
public async Task<Unit> Handle(AddNoteCommand<TEntity> request, CancellationToken cancellationToken)
{
await _repo.Insert(new TEntity
{
CreatedByUserId = request.CurrentUserProfileId,
CreatedDate = _time.Current,
NoteContent = request.NoteContent,
ParentId = request.ParentId
});
await _repo.SaveChanges();
return Unit.Value;
}
}
Registration with AutoFac
public static ContainerBuilder AddGenericHandlers(this ContainerBuilder builder)
{
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterGeneric(typeof(AddNoteCommandHandler<>)).AsImplementedInterfaces();
return builder;
}
If anybody knows of a way to do this in the default asp.net core DI container then please show me!
It is my understanding (and I tested this) that explicitly registering the GenericHandlerBase isn't necessary... If you are going to create concrete handlers for each generic type for that handler then MediatR will find the correct handler without issue..
Here is an example of this:
Here is the registration using Default DI Container:
This works just fine... Now imagine if you had multiple entities, and further more, imagine if you had multiple service dependencies for your handler. While even though you would not have to implement the handle method for each derived handler you still need to create a concrete class that closes the generic type and pass all dependencies to the base constructor... In my opinion that is very tedious to have to do that for every entity and every service dependency.. In my opinion it is much simpler to simply register the generic handler via Autofac like I showed above. But if not using a third party DI container then by all means create the concrete handlers... If I couldn't use a third party di container for whatever reason then I would do it this way..
Cheers.
Are there any examples to show how to make a generic IRequestHandler?
I can obviously add lots of interfaces such as
but cannot find any examples of a generic handler to handle different requests and responses in the same handler. Is something like the following possible?
which would handle something like the following