Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
I was happy and surprised to see that MediatR v3.0 was released yesterday. One of the new features, which I was really looking forward to are pipeline behaviors.A pipeline behavior is an implementation of IPipelineBehavior<TRequest, TResponse>
. It represents a similar pattern to filters in ASP.NET MVC/Web API or pipeline behaviors in NServiceBus.
Changes
There are a couple major breaking changes to v3. All of them I’m fairly happy about actually.Messages
There is no distinction between sync, async (cancellable) requests or notifications. You simply implement eitherIRequest
or INotification
.
You must still create the appropriate handler via either IRequestHandler
, IAsyncRequestHandler
, ICancellableAsyncRequestHandler
.
Async
To send a request or publish a notification simply useTask Send(IRequest)
or Task Publish(INotification)
No longer does it have the “Async” convention of appending to the method name. I know this is often debated about this convention. I don’t mind it at all since there are no sync methods.
Behaviors
The new addition is the interfaceIPipelineBehavior<TRequest, TResponse>
This allows you to create implementation(s) that will invoked in the order they are registered with your container (returned from the MultiInstanceFactory
delegate).
The simplest implementation, that does nothing but call the next possible behavior.
You can see how you can use this to create Pre and Post behaviors to create a pipeline.
Pre & Post Processors
Thankfully, these have already been created and built-in to v3.RequestPreProcessorBehavior<TRequest, TResponse>
and RequestPostProcessorBehavior
<TRequest, TResponse>
to the rescue.
These can be used to implementing the appropriate interface of IRequestPreProcess<TRequest,TResponse>
and IRequestPostProcessor<TRequest,TResponse>
Note: Be sure to register your IRequestPreProcess<TRequest,TResponse>
and IRequestPostProcessor<TRequest,TResponse>
as well as RequestPreProcessorBehavior<TRequest, TResponse>
and RequestPostProcessorBehavior
<TRequest, TResponse>
with your container.
Derek, thanks so much for the quick and timely blog in MediatR 3.0. I forked and pulled the code locally last night, and was exploring the new IPipelineBehavior interface, but looks like you beat me to the punch with a blog post. I’ll definitely be using your blog as a guide to implementing pipeline behavior in MediatR 3.0 for an ASP.NET Core project I’ve been working on.
Thanks. Don’t stop this post from creating your own. There’s likely value in creating your own post about your experience using the new behaviors.
I love MediatR, but we haven’t gone all async yet so I’m not switching to v3 until we’re ready for that. I guess it’s nice MediatoR has its own behaviors, but I’ve been behaviors for a while through our SimpleInjector container decorators so no rush on that.
I was writing about MediatR the other day (which turned out to be a little late) comparing it with MicroBus (which is the mediator I’ve been writing). You can check it out here https://www.lavinski.me/comparing-microbus-and-mediatr/, I had to update the post after finding this https://github.com/jbogard/MediatR/wiki/Behaviors
I’ll take a look! Thanks for sharing.
I don’t think the “AddToCartPostHandler” example above would be a good way to implement a behaviour. It works fine for a single behaviour, but if you have multiple then it could have unintended side effects, especially if the order of execution matters.
As an example, my 2.x pipeline has 2 “pre-behaviours” – one that handled authorization (does the user have permission to do this) and one that handled validation (is the IRequest in a valid state). It is important that these are executed in order otherwise the errors returned could result in security issues, giving out unintended information about your api.
In v3, i implement a generic authorization behaviour and a validation behaviour, which then accept an IAuthorizor and IValidator respectively. These behaviours are added to DI in the order i want them to execute.
Does this sound like something I should be doing, or is it possibly over engineering the solution?
Yes the order in which you register with your DI Container matters, as you mentioned. As with my example post on creating a decorator using v2, you could basically do the same thing in v3 by implementing IPipelineBehavior and having your implementation take collections of IValidator and IAuthorizor and then invoke them prior to calling the next behavior. Meaning you could have one implementation of IPipelineBehavior that worked like your v2 did.
I am still getting the cannot resolve IRequestHandler`2 when using the IAsyncRequestHandler but it works fine for IRequestHandler. Any ideas?
Is there a use case for the Behaviors in an older app with no DI Container?