Mediatr: Sending extra information to Handler

Created on 30 Jan 2019  路  15Comments  路  Source: jbogard/MediatR

We pass the request model to the Handler via. Send(). What If I would want to pass more information which is not there in my model. Like I would want to pass some information which is there in request headers/routes.

I can get around this by adding properties to the input model and hide them in swagger documentation and populate them before invoking the Send method. But is there a pattern which Mediator recommends.?

Most helpful comment

You might be able to create a "wrapper request":

public class ContextualRequest<TRequest, TResponse> : IRequest<TResponse>
    where TRequest : IRequest<TResponse>
{
    public ContextualRequest(TRequest data, string userName)
    {
        Data = data;
        UserName = userName;
    }

    public TRequest Data { get; }

    public string UserName { get; }
}

And then set your handler up like this:

public class MyRequestHandler
    : IRequestHandler<ContextualRequest<TRequest, TResponse>, TResponse>
{
    public Task<TResponse> Handle(
        ContextualRequest<TRequest, TResponse> request,
        CancellationToken cancellationToken)
    {
        // TODO - Use the Data here.
    }
}

And then use it like this:

var request = new ContextualRequest<MyRequest, MyResponse>(data, "From header");
var response = _mediator.Send(request, CancellationToken.None);

All 15 comments

I have come across a few situations myself where it would be nice if Send/Publish has an (optional) 'context' parameter of type 'object', which flows to behaviors and handlers.

"Solved" it by putting those on the model, but it would indeed be cleaner if MediatR has something for this.
It would be an easy change I think, but a very breaking one.

Something like:

public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, object context = null, CancellationToken cancellationToken = default)

@Sandeep321 You can also (and probably should) use different models for your public API than for your internal MediatR requests.

@lilasquared Agreed to that, but that will introduce a mapping layer in between. The intention here was to find out a way to pass information to handler out of request model.

@remcoros Exactly, maybe @jbogard will have some idea/say on this.

Maybe you could inject some kind of extractor class in the handler that extracts this information from the request context? F.e. we inject a IdentityExtractor class that enables us to extract the ClaimsIdentity from the current request.

Another way is indeed to add the extra data to a Headers field of the request. We're doing this here and autogenerate the API models, the request/command models and the mapping between both using T4 templates.

@huysentruitw Yes, these options are always open. I was looking for sending a type of object via Mediator, propabply that can be optional. Thanks for reply anyway. :

Are dependencies a possibility? That your handler takes a dependency on the current request context? That would have all the headers.

@jbogard Handler wouldn't have HttpRequest Context, In fact, it would have the Query or Command object. That's where the trick lies.
One more thing, it's not just about the info in headers, but passing any other additional information to the handler for that matter.

A handler still has a constructor and can have any number of dependencies. The command or query is passed to the handle method but you get to define what dependencies are injected through the constructor.

You might be able to create a "wrapper request":

public class ContextualRequest<TRequest, TResponse> : IRequest<TResponse>
    where TRequest : IRequest<TResponse>
{
    public ContextualRequest(TRequest data, string userName)
    {
        Data = data;
        UserName = userName;
    }

    public TRequest Data { get; }

    public string UserName { get; }
}

And then set your handler up like this:

public class MyRequestHandler
    : IRequestHandler<ContextualRequest<TRequest, TResponse>, TResponse>
{
    public Task<TResponse> Handle(
        ContextualRequest<TRequest, TResponse> request,
        CancellationToken cancellationToken)
    {
        // TODO - Use the Data here.
    }
}

And then use it like this:

var request = new ContextualRequest<MyRequest, MyResponse>(data, "From header");
var response = _mediator.Send(request, CancellationToken.None);

@pendragon-andyh Looks like an approach. Thanks :)

IMO having a context would break the event sourcing implementations around mediatr where no matters when a command or a request here was sent I think if something is needed from an external source it should be injected in the handler ?

Just a thoughts.... :) Kindly

@davidrevoledo It's really ironic, but I came here because I wanted to pass event id alongside the request in my event sourcing implementation :)

@0xorial yes could be a case , but some event sourcing implementation are not using the same process, I've implemented myself mediatr in some projects where we dispatch requests to a queue and some job read them there the context could be a problem and is something that could be stored I guess is part of the request itself, but as I said I said that for those kind of implementation, it could vary I guess.

Was this page helpful?
0 / 5 - 0 ratings