question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to register IRequestHandler<,> to multiple Implementations with type constraints.

See original GitHub issue

Hello I’ve been following the addition of @jbogard to .net 5.0 dependency injection and was wondering if it is all sorted out now.

I am trying to register a generic Handler for generic requests…

I am currently using asp.net core 3.1 and MediatR 8.1

//generic command
  public class AddNoteCommand<TEntity>  
        where TEntity : class, INoteEntity
    {
        public int ParentId { get; set; }
        public int CurrentUserProfileId { get; set; }
        public string NoteContent { get; set; }
    }
    
//generic 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;
        }
    }

//another implementation 
//command
public class FetchAllNotesForParentQuery<TEntity> 
        where TEntity : class, INoteEntity
    {
        public int ParentId { get; set; }
    }

//handler
public class FetchAllNotesForParentQueryHandler<TEntity> : IRequestHandler<FetchAllNotesForParentQuery<TEntity>, List<TEntity>>
        where TEntity : class,  INoteEntity
        
    {
        private readonly INoteRepository<TEntity> _repo;

        public FetchAllNotesForParentQueryHandler(INoteRepository<TEntity> repo)
        {
            _repo = repo;
        }

        public Task<List<TEntity>> Handle(FetchAllNotesForParentQuery<TEntity> request, CancellationToken cancellationToken)
        {
            return _repo.FetchByParentId(request.ParentId);
        }
    }

//usage **MyNoteClass implements INoteEntity
await _mediator.Send(new AddNoteCommand<MyNoteClass>
 {
    //properties here... 
 });
 var notesList = await _mediator.Send(new FetchAllNotesForParentQuery<MyNoteClass> 
{
    ParentId = parentId
});

//asp.net core DI registration
services
    .AddTransient(typeof(IRequestHandler<,>), typeof(AddNoteCommandHandler<,>))
    .AddTransient(typeof(IRequestHandler<,>), typeof(FetchAllNotesForParentQueryHandler<,>);

Will this work somehow?

I’ve tried this and it seems to try and get the last implementation I register at runtime and fails letting me know that the type parameter violates the constraints of the implementation it tries to use.

It always seems to try and use the last implementation that I register.

Will this be solved by upgrading to .net 5.0? or maybe mediator 9+ ?

Any help would be appreciated. Thank you!

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:5

github_iconTop GitHub Comments

1reaction
zachpainter77commented, Jun 28, 2021

@abuzaforfagun

This sample is registered without having to explicitly register the handlers by using the default AddMediatr extension method. Since they are concrete handlers derived from a closed generic base class, Mediatr is able to find the correct handler without any explicit service registration.

@OculiViridi

One way I’ve found to achieve it is by creating concrete handlers that are derived from the generic ones.

The downside to this is that you still have to write the definition of each handler. Also, you would still need to repeat the constructors for each one of them as well. This can be quite tedious, especially if you have more than one type parameter, more implementations and more dependencies to inject.

But at least this way you wouldn’t have to rewrite the handler implementation.

I don’t think there is any way of getting around this at the moment besides using a third party DI container. I think this is because the default .NET DI container does not support generic variance. So, until they do I’m just going to use Autofac which works quite well along side the default container.

For example:

//Three implementations of INoteEntity
public class NoteType1 : INoteEntity { }
public class NoteType2 : INoteEntity { }
public class NoteType3 : INoteEntity { }

//Three concreate handlers. :(
public NoteType1Handler : FetchAllNotesForParentQuery<NoteType1>
{
    //constructor still have to rewrite for each implementation
    public NoteType1Handler(INoteRepository<NoteType1> repo) : base(repo) {}
}
public NoteType2Handler : FetchAllNotesForParentQuery<NoteType2>
{
    //constructor still have to rewrite for each implementation
    public NoteType2Handler(INoteRepository<NoteType2> repo) : base(repo) {}
}
public NoteType3Handler : FetchAllNotesForParentQuery<NoteType3>
{
    //constructor still have to rewrite for each implementation
    public NoteType3Handler(INoteRepository<NoteType3> repo) : base(repo) {}
}

public class FetchAllNotesForParentQuery<TEntity> 
    where TEntity : class, INoteEntity
{
    public int ParentId { get; set; }
}

public class FetchAllNotesForParentQueryHandler<TEntity> : IRequestHandler<FetchAllNotesForParentQuery<TEntity>, List<TEntity>>
    where TEntity : class,  INoteEntity
{
    private readonly INoteRepository<TEntity> _repo;
    public FetchAllNotesForParentQueryHandler(INoteRepository<TEntity> repo)
    {
        _repo = repo;
    }

    public Task<List<TEntity>> Handle(FetchAllNotesForParentQuery<TEntity> request, CancellationToken cancellationToken)
    {
        return _repo.FetchByParentId(request.ParentId);
    }
}

The best way in my opinion is to not worry about any of that… Just simply create your generic commands / handlers and then register them using autofac.

I actually got this working with AutoFac as a container.

public static ContainerBuilder AddGenericHandlers(this ContainerBuilder builder)
        {
            builder.RegisterSource(new ContravariantRegistrationSource());
            builder.RegisterGeneric(typeof(FetchDropdownItemByEnumQueryHandler<>)).AsImplementedInterfaces();
            builder.RegisterGeneric(typeof(FetchAllNotesForParentQueryHandler<>)).AsImplementedInterfaces();
            builder.RegisterGeneric(typeof(FetchAllNotesWithTypeForParentQueryHandler<>)).AsImplementedInterfaces();
            builder.RegisterGeneric(typeof(FetchAllSelectListItemsQueryHandler<>)).AsImplementedInterfaces();
            builder.RegisterGeneric(typeof(AddNoteCommandHandler<>)).AsImplementedInterfaces();
            builder.RegisterGeneric(typeof(AddNoteWithTypeCommandHandler<>)).AsImplementedInterfaces();
            return builder;
        }

Is there currently a way to do this with the default .Net 5.0 DI container?

This issue can be closed, I guess I’m just looking for a concrete answer if this is possible with the default container.

Thanks.

1reaction
abuzaforfaguncommented, Jun 22, 2021

@zachpainter77

public class Command<T> : IDistributedCommand, IRequest<bool> where T : class, IDistributedCommand
{
    public T Data { get; }
    public OutBoxStatus Status { get; }

    public Command(T data, OutBoxStatus status)
    {
        Data = data;
        Status = status;
    }
}

services.AddTransient<IRequestHandler<Outbox.Command<CreateInvoiceRequest.CreateInvoiceRequestCommand>, bool>, Outbox.CommandHandler<CreateInvoiceRequest.CreateInvoiceRequestCommand>>();
            services.AddTransient<IRequestHandler<Outbox.Command<CreateInvoiceRequest.UpdateInvoiceRequestCommand>, bool>, Outbox.CommandHandler<CreateInvoiceRequest.UpdateInvoiceRequestCommand>>();

This worked for me to register multiple generic request handlers. But need to do the implicitly. Is anyone having any idea to register the DI in a generic way?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Registering commands and handlers with generic interfaces
I'm studying about the mediator pattern. I would like to register a handler to handle a command that has a generic type, where...
Read more >
CQRS and MediatR in ASP.NET Core
We first define a LoggingBehavior class, taking two types of parameters TRequest and TResponse , and implementing the IPipelineBehavior<TRequest ...
Read more >
Diagnosing and Fixing MediatR Container Issues
These types are all resolved from a container at runtime as MediatR ... add a registration for the requested service/implementation types:
Read more >
Registering Mediatr handlers with open generic request types
Solution 1: Register each type explicitly ... One way we can register the generic class without having to define concrete classes is by ......
Read more >
Command & Query: Domain - Request handler mediator
Request handler mediator explanation and implementation. ... Type of request argument changed to IRequest<TResponse> — to keep the same type constraint.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found