Search code examples
c#asynchronous.net-coreasync-awaitmediatr

MediatR Request Handler works with async but doesn't work if I Remove it


I'm attempting to use the MediatR Nuget package in a small test app. I've had a few problems that I've been able to find solutions to and this solution has me a little stump.

This is a very simple test Web app I built to learn how to use the library.

In my HomeController I make the following call.

Customers = await _mediator.Send(new GetCustomersQuery(0))

My oringal code looked like this.

public  Task<List<Customer>> Handle(GetCustomersQuery request, CancellationToken cancellationToken)
    {
         return new Task<List<Customer>>(() => {
            if(request.Id == 0)
            {
                return _repo.GetAllCustomers().ToList();
            }
            else
            {
                return new List<Customer>() { _repo.GetCustomerById(request.Id) };
            }
         });
   }

The behavior I experienced is once it hit the await send call in my controller it would just sit there and wait.

I tried stepping through the code and I'm looking at the Send method in the Mediator class that looks like this:

var handler = (RequestHandlerWrapper<TResponse>)_requestHandlers.GetOrAdd(requestType,
            static t => (RequestHandlerBase)(Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(t, typeof(TResponse)))
                                             ?? throw new InvalidOperationException($"Could not create wrapper type for {t}")));

This code finds my handler and calls the Handle method and executes. If I step over the call and hover over the handler variable I get an text saying the name 'handler' does not exist in the current context. I don't know if that is just a quirk of the debugger. (I'm using Rider currently). The next call is as follows.

return handler.Handle(request, cancellationToken, _serviceFactory);

If I step into or over that line it takes me back to the caller and just sits likes its waiting for the async method to complete, which I assume that's what it is doing.

I played around a little bit and looked at some other code people had used themselves and tried this in my handler instead.

public async Task<List<Customer>> Handle(GetCustomersQuery request, CancellationToken cancellationToken)
    {
        // return new Task<List<Customer>>(() => {
        //     if(request.Id == 0)
        //     {
        //         return _repo.GetAllCustomers().ToList();
        //     }
        //     else
        //     {
        //         return new List<Customer>() { _repo.GetCustomerById(request.Id) };
        //     }
        // });

         return request.Id == 0 ? _repo.GetAllCustomers().ToList() : new List<Customer>() { _repo.GetCustomerById(request.Id) };

    }

This time in the Send method the handler variable still gives me the same message as before when I hover over it.

The return handler.Handle(request, cancellationToken, _serviceFactory); executes this time and a value returns and information displays on my page.

I don't quite know why my first method doesn't work. I don't know if there are just too many await calls stringed together causing problems or if I'm just straight up missing some basic idea of async programming in C# that my brain can't drum up. The way that works kinda bugs me because my understanding is that putting async in front of the return type in the method is for using an await call in that method. If I remove that my program no longer builds and if I wrap that logic in a new task I run into the same issue.

Can anyone help me understand or point to some information that will help me to better understand?


Solution

  • Thanks to Thomas Schmidt. He answered my question and as I thought it was very simple.

    Instead of generating a new task I should have been using something like this:

    return Task.FromResult<List<Customer>>(request.Id == 0 ? _repo.GetAllCustomers().ToList() : new List<Customer>() { _repo.GetCustomerById(request.Id) });
    

    Thank you.