Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mediatr with generic handler and query

I am working on a ASP.NET Core 2.2 Web API application with Mediatr.

I have a handler that looks like -

public class MyQueryHandler<T> : IRequestHanlder<MyQuery<T>, IQueryable<T>>
{
   public Task<IQueryable<T>> Handle(MyQuery<T> myquery, CancellationToken cancellationToken)
   {
       //perform query

       IQueryable<T> models = someDatabaseQuery.ProjectTo<T>();
   }       
}

This is the query -

public class MyQuery<T> : IRequest<IQueryable<T>>
{
   //some properties
}

When I try to make a request like this -

var result = await _mediator.Send(new MyQuery<SomeModel> {/* set the properties on the query */})

I get an exception -

An unhandled exception occurred while processing the request.

InvalidOperationException: Handler was not found for request of type MediatR.IRequestHandler`2[MyQuery`1[SomeModel],System.Linq.IQueryable`1[SomeModel]]. Register your handlers with the container. See the samples in GitHub for examples.

I have spent quite a few hours tried a many things but none have worked. I even tired using Autofac along side the service collection, following the example provided in the Mediator github repo.

like image 684
Bryan Avatar asked Nov 06 '22 14:11

Bryan


1 Answers

Each Query should have a concrete type/flat structure so the handler for it can be easily registered by the Dependency Injection container at the runtime. I believe registering the generic query handler as you gave as an example is just impossible as the DI container can have problems with registration generic types. I believe creating a Behavior is the right thing you should do. It can give you a possibility to handle all queries or commands in one place, so you can run some extra/generic logic like logging stuff, etc. before you hit the handler of given Query/Command.

EDIT

In the handler I use automapper projections to limit what is queried from the db table in question. The lets the caller tell query and in turn the handler the shape of data wanted.

To limit what is queried from the database I would use an approach of creating a query and query handler per entity. I think it makes sense to have such a separation as from security perspective you may want to give access to only a specific group of users for running given queries.

So the example for e.g. Order entity would look like.

public class OrderDto
{
    public string Name { get; set; }

    public int Amount { get; set; }
}

public class FilterOrdersQuery : IRequest<List<OrderDto>>
{
    public string Filter { get; set; }
}

public class FilterOrdersQueryHandler : IRequestHandler<FilterOrdersQuery, List<OrderDto>>
{
    public Task<List<OrderDto>> Handle(FilterOrdersQuery notification, CancellationToken cancellationToken)
    {
        var dataSource = new List<OrderDto>(){
            new OrderDto()
            {
                Name = "blah",
                Amount = 65
            },
            new OrderDto()
            {
                Name = "foo",
                Amount = 12
            },
        };

        var result = dataSource
            .Where(x => x.Name.Contains(notification.Filter))              
            .ToList();

        return Task.FromResult(result);
    }
}

This is only a simple example which shows how to filter the given entity and return List of filtered objects. You can also add logic for pagination, OrderBy, etc. etc.

like image 97
GoldenAge Avatar answered Nov 15 '22 01:11

GoldenAge