Executing MediatR PreProcessor Only for Specific Interface Types (Commands) - c#

[NOTE: This is a "replacement" question. The first one was based on my main project's code so I've redone the question with code from a single-purpose project that illustrates the principle more cleanly. The question remains the same, just better presented.]
The Scenario
I'm trying to setup a command pre-processor on a CQRS request pipeline using MediatR pipeline behaviors and Autofac for request routing. My goal is for the pre-processor to run only for commands (ICommand<>) as opposed to all requests (IRequest<>), which will result in the pre-processor executing for commands, queries and events.
The Issue
I can get my GenericPreProcessor or any other pre-processor to run fine for all types of requests, but any method I've used to try to "filter" the injection either returns an error or simply doesn't execute the desired pre-processor.
My working-for-all-requests pipeline configuration in Autofac looks like this:
// Pipeline pre/post processors
builder
.RegisterGeneric(typeof(RequestPostProcessorBehavior<,>))
.As(typeof(IPipelineBehavior<,>));
builder
.RegisterGeneric(typeof(RequestPreProcessorBehavior<,>))
.As(typeof(IPipelineBehavior<,>));
// Works as desired: Fires generic pre-processor for ALL requests, both cmd and query
builder
.RegisterGeneric(typeof(GenericRequestPreProcessor<>))
.As(typeof(IRequestPreProcessor<>));
// Works for all requests, but I need a way to limit it to commands
builder
.RegisterGeneric(typeof(MyCommandPreProcessor<>))
.As(typeof(IRequestPreProcessor<>));
Conceptually I'm trying to do something like any of these, which fail:
builder
.RegisterGeneric(typeof(MyCommandPreProcessor<>)) // Note generic
.As(typeof(IRequestPreProcessor<ICommand<>>));
// Intellisense error "Unexpected use of an unbound generic"
builder
.RegisterType(typeof(MyCommandPreProcessor)) // Note non-generic
.As(typeof(IRequestPreProcessor<ICommand<>>));
// Intellisense error "Unexpected use of an unbound generic"
builder
.RegisterType(typeof(MyCommandPreProcessor)) // Note non-generic
.As(typeof(IRequestPreProcessor<ICommand<CommonResult>>));
// No errors, but MyCommandPreProcessor not firing
I'm trying a couple of different configurations for MyCommandPreProcessor, a generic and a non-generic but am stumped with either:
public class MyCommandPreProcessor<TRequest> : IRequestPreProcessor<TRequest>
{
public Task Process(TRequest request, CancellationToken cancellationToken)
{
Debug.WriteLine("***** MYCOMMAND PREPROCESSOR CALLED *****");
return Task.CompletedTask;
}
}
- OR -
public class MyCommandPreProcessor : IRequestPreProcessor<IRequest<ICommonResponse>>
{
public Task Process(TRequest request, CancellationToken cancellationToken)
{
Debug.WriteLine("***** MYCOMMAND PREPROCESSOR CALLED *****");
return Task.CompletedTask;
}
}
My Question
Any ideas on how I can register a pre-processor that will be restricted to only fire for IRequest<> types that are closed types of ICommand<>?
Supporting Materials
Project on GitHub
The entire minimal sample project can be viewed or cloned at https://github.com/jhoiby/MediatRPreProcessorTest
Autofac MediatR Config
A working config, with a single GenericRequestPreProcessor for all requests.
builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly).AsImplementedInterfaces();
var mediatrOpenTypes = new[]
{
typeof(IRequestHandler<,>),
typeof(IRequestHandler<>),
typeof(INotificationHandler<>)
};
foreach (var mediatrOpenType in mediatrOpenTypes)
{
// Register all command handler in the same assembly as WriteLogMessageCommandHandler
builder
.RegisterAssemblyTypes(typeof(MyCommandHandler).GetTypeInfo().Assembly)
.AsClosedTypesOf(mediatrOpenType)
.AsImplementedInterfaces();
// Register all QueryHandlers in the same assembly as GetExternalLoginQueryHandler
builder
.RegisterAssemblyTypes(typeof(MyQueryHandler).GetTypeInfo().Assembly)
.AsClosedTypesOf(mediatrOpenType)
.AsImplementedInterfaces();
}
// Pipeline pre/post processors
builder.RegisterGeneric(typeof(RequestPostProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(RequestPreProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(GenericRequestPreProcessor<>)).As(typeof(IRequestPreProcessor<>));
// builder.RegisterGeneric(typeof(GenericRequestPostProcessor<,>)).As(typeof(IRequestPostProcessor<,>));
// builder.RegisterGeneric(typeof(GenericPipelineBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.Register<SingleInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => c.Resolve(t);
});
builder.Register<MultiInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
});
MyCommandPreProcessor Class
I'm experimenting with both of these, generic and non-generic:
public class MyCommandPreProcessor<TRequest> : IRequestPreProcessor<TRequest>
{
public Task Process(TRequest request, CancellationToken cancellationToken)
{
Debug.WriteLine("***** MYCOMMAND PREPROCESSOR CALLED *****");
return Task.CompletedTask;
}
}
- AND -
public class MyCommandPreProcessor : IRequestPreProcessor<IRequest<ICommonResponse>>
{
public Task Process(TRequest request, CancellationToken cancellationToken)
{
Debug.WriteLine("***** MYCOMMAND PREPROCESSOR CALLED *****");
return Task.CompletedTask;
}
}
Inheritance Structures
// Requests
IMediatR.IRequest<TResponse>
<- IMessage<TResponse>
<- ICommand<TResponse>
<- concrete MyCommand : ICommand<CommonResponse>
<- IQuery<TResponse>
<- concrete MyQuery : IQuery<CommonResponse>
// Request Handlers
IMediatR.IRequestHandler<in TRequest,TResponse>
<- IMessageHandler<in TRequest,TResponse>
<- ICommandHandler<in TRequest,TResponse>
<- concrete MyCommandHandler : ICommandHandler<MyCommand,CommonResponse>
<- IQueryHandler<In TRequest,TResponse>
<- concrete MyQueryHandler : IQueryHandler<MyQuery,CommonResponse>
// CommonResponse - A POCO that returns result info
ICommonResponse
<- concrete CommonResponse
Commands
public interface IMessage<TResponse> : MediatR.IRequest<TResponse>
{
}
public interface ICommand<TResponse> : IMessage<TResponse>
{
}
public class MyCommand : ICommand<CommonResponse>
{
}
Command Handlers
public interface IMessageHandler<in TRequest, TResponse>
: MediatR.IRequestHandler<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
}
public interface ICommandHandler<in TRequest, TResponse>
: IMessageHandler<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
}
public class MyCommandHandler : ICommandHandler<MyCommand, CommonResponse>
{
public async Task<CommonResponse> Handle(
MyCommand request,
CancellationToken cancellationToken)
{
Debug.WriteLine(" ***** Command handler executing *****");
return
new CommonResponse(
succeeded: true,
data: "Command execution completed successfully.");
}
}
PreProcessor Injection Target (in the MediatR Pipeline Code)
The constructor that receives the injected IRequestPreProcessor<> is:
public RequestPreProcessorBehavior(IEnumerable<IRequestPreProcessor<TRequest>> preProcessors)
{
...
}
It can be seen on Github on line 17 of the file at:
https://github.com/jbogard/MediatR/blob/master/src/MediatR/Pipeline/RequestPreProcessorBehavior.cs
Thank you!

I have the same exact scenario as you and I believe the issue is stemming from RequestPreProcessorBehavior<TRequest, TResponse> not passing all types down to IRequestPreProcessor<TRequest>.
You either:
No constraints: check the type of request in MyCommandPreProcessor<TRequest> in every IRequestPreProcessor:
public Task Process(TRequest request, CancellationToken cancellationToken)
{
var isCommand = typeof(TRequest).GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICommand<>));
if (isCommand)
{
// Magic
}
}
Create your own pre-processing behavior that exposes TRequest of IPipelineBehavior<TRequest, TResponse>:
public interface IRequestPreProcessor<in TRequest, TResponse> : IRequestPreProcessor<TRequest>
where TRequest : IRequest<TResponse>
{
}
public class MyRequestPreProcessorBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IRequestPreProcessor<TRequest, TResponse>> _preProcessors;
public RequestPreProcessorBehavior(IEnumerable<IRequestPreProcessor<TRequest, TResponse>> preProcessors)
{
_preProcessors = preProcessors;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
foreach (var processor in _preProcessors)
{
await processor.Process(request, cancellationToken).ConfigureAwait(false);
}
return await next().ConfigureAwait(false);
}
}
With option 2, you would add constraints to any class that implemented IRequestPreProcessor<TRequest, TResponse> for command / query specific pre-processors.

Just add the interface on command/query class and add the preprocessor for that interface.
Please go through the below example
public interface IUser{}
public class AddUserCommand: IUser, IRequest<UserModel>{....
public class UpdateUserCommand: IUser, IRequest<UserModel>{....
public class GetUserQuery: IRequest<UserModel>{....
remember: here in above classes GetUserQuery class don't have IUser interface.
Now lets create the pre-processor class which execute only for command/query which marked with IUser interface
public class UserCommandQueryPrepProcessor<T>: IRequestPreProcessor<T> where T: IUser
{.....
public Task Process(T request, CancellationToken token)
{ //enter your interface specific logic here
}
}
Note: above UserCommandQueryPrepProcessor class is a generic class with where IUser which executes only for class 'AddUserCommand' and 'UpdateUserCommand' inheriting IUser interface.

Related

MediatR pipeline that saves a generic DbContext after each command

I am trying to create a MediatR pipeline which calls the SaveChangesAsync on my DbContext after each command. I am trying to make this work in a generic way, so that I can add the pipeline with DI, and it will work for all my commands. By default, I want to save it, but I want to have the option in my command to set a boolean, and if it is set to false, I do not want to call the SaveChangesAsync on my DbContext. I have basically come to a working solution, but in my DI registration, I have to specify which DbContext I am using, which I want to prevent.
I have written the following code:
SaveCommandBehavior:
public class SaveCommandBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IRequestHandler<TRequest, TResponse> _requestHandler;
public SaveCommandBehavior(IRequestHandler<TRequest, TResponse> requestHandler)
{
_requestHandler = requestHandler;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var response = await next();
var isDbCommandHandler = IsAssignableToGenericType(_requestHandler.GetType(), typeof(DbCommandHandler<>));
if (isDbCommandHandler &&
_requestHandler is DbCommandHandler<MyDbContext> commandHandler &&
request is DbCommand { SaveChanges: true } or DbCommand<TResponse> { SaveChanges: true })
{
await commandHandler.DbContext.SaveChangesAsync(cancellationToken);
}
return response;
}
public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
{
if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
return true;
}
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return IsAssignableToGenericType(baseType, genericType);
}
}
Registration:
public static IServiceCollection AddSaveCommandPipelineLocal(this IServiceCollection services)
{
return services.AddTransient(typeof(IPipelineBehavior<,>), typeof(SaveCommandBehavior<,>));
}
Abstract classes that I use in my commands:
public abstract class DbCommand: ICommand
{
public bool SaveChanges { get; set; } = true;
}
public abstract class DbCommand<TResponse> : ICommand<TResponse>
{
public bool SaveChanges { get; set; } = true;
}
public abstract class DbCommandHandler<TDbContext> where TDbContext : DbContext
{
public TDbContext DbContext;
protected DbCommandHandler(TDbContext dbContext)
{
DbContext = dbContext;
}
}
Example command and handler:
public sealed class MyCommand: DbCommand
{
public Guid Parameter1 { get; set; }
}
public class MyCommandHandler: DbCommandHandler<MyDbContext>, ICommandHandler<MyCommand>
{
public MyCommandHandler(MyDbContext dbContext)
: base(dbContext)
{
}
public async Task<Unit> Handle(MyCommand request, CancellationToken cancellationToken)
{
// Command logic making changes to EF, without calling save.
}
}
While this code would work, I have to use
_requestHandler is DbCommandHandler commandHandler
which I do not want: I already know at that point that I am dealing with a _requestHandler which should have a DbContext of base type 'DbContext', which should be enough information to call 'SaveChangesAsync' on it. However, I can not figure out how to get that type, without having to specify which DbContext I am dealing with.
I am able to figure out whether or not it is a valid type with a DbContext of any type, using the isDbCommandHandler variable, but I can then not utilise that to cast this to a usable variable of which I can use the DbContext.
I feel like I am overlooking something simple. I feel like I should add a parameter TContext to SaveCommandBehavior where TContext : DbContext, so I can use that in the cast, but then I have an extra generic parameter in my SaveCommandBehavior which would make my DI fail, since the PipelineBehavior only takes 2 parameters
A proposed alternative more than a direct answer.
What if you don't get the DbContext form the handler, but you inject it in the pipeline?
You can still check for the abstract class / interface for the bool just to know if you need to save, but you can get rid of the dependency between the handler and the dbcontext.
public class SaveCommandBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IRequestHandler<TRequest, TResponse> _requestHandler;
private readonly MyDbContext _myDbContext;
public SaveCommandBehavior(
MyDbContext myDbContext,
IRequestHandler<TRequest, TResponse> requestHandler)
{
_requestHandler = requestHandler;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var response = await next();
var isDbCommandHandler = IsAssignableToGenericType(_requestHandler.GetType(), typeof(DbCommandHandler<>));
if (isDbCommandHandler &&
request is DbCommand { SaveChanges: true } or DbCommand<TResponse> { SaveChanges: true })
{
await _myDbContext.SaveChangesAsync(cancellationToken);
}
return response;
}
...
}
The only problem that I see with this is that right now you are using only one dbcontext which is most likely something is the case for most projects.
In case you want to use multiple DBcontexts you would have to inject a factory instead of MyDbContext.
That factory would have to get the DbContext With the service provider like
serviceProvider.GetRequierdService<MyDbContext>();
interface IDbContextFactory{
DbContext GetDBContextForType(Type type);
}
class DbContextFactory : IDBContextFactory{
DbContext GetDBContextForType(Type type){
//Some custom logic here
}
}

Adding middleware to bot framework

I am trying to add middleware into echo bot, that converts message into lower cases.
I have created Middleware class that inherits from IMiddleware
public class MiddlewareOne : IMiddleware
{
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default)
{
if(turnContext.Activity.Type == ActivityTypes.Message)
{
Debug.WriteLine(turnContext.Activity.Text);
turnContext.Activity.Text = turnContext.Activity.Text.ToLower();
await next(cancellationToken);
Debug.WriteLine(turnContext.Activity.Text);
}
else
{
await next(cancellationToken);
}
}
}
}
Now I am trying to add it into Startup.cs file. I found somewhere it should be added as Transient.
services.AddTransient<MiddlewareOne>();
Still, it's not working. I think MiddlewareOne class is okay, but how should I configure it in Startup.cs file?
Thank you
You have to register the middleware in your BotFrameworkAdapter descendant (e.g. BotFrameworkHttpAdapter) by calling the Use method in constructor. You can pass the middleware as a constructor parameter and DI will take care of activation.
An example (made without VS assistance)
public class MyAdapter : BotFrameworkHttpAdapter
{
public MyAdapter(MiddlewareOne mw1, IConfiguration configuration, ILogger<BotFrameworkHttpAdapter> logger)
: base(configuration, logger)
{
Use(mw1);
// other code..
}
}

Add a generic handler for Send and Publish methods of the MediatR library in asp .net core

I use the CQS pattern in my asp.net core project. Let's start with an example to better explain what I want to achieve. I created a command:
public class EmptyCommand : INotification{}
The command handler:
public class EmptyCommandHandler : INotificationHandler<EmptyCommand>
{
public Task Handle(EmptyCommand notification, CancellationToken cancellationToken)
{
return Task.FromResult(string.Empty);
}
}
The query:
public class EmptyQuery : IRequest<string>{}
The query handler:
public class EmptyQueryHandler : IRequestHandler<EmptyQuery, string>
{
public Task<string> Handle(EmptyQuery notification, CancellationToken cancellationToken)
{
return Task.FromResult(string.Empty);
}
}
and this is a simple example of how to run the command and query and invoke the Handle method from the EmptyCommandHandler and EmptyQueryHandler:
readonly IMediator _mediator;
public HomeController(IMediator mediator)
{
_mediator = mediator;
}
public async Task<IActionResult> Index()
{
await _mediator.Publish(new EmptyCommand());
var queryResult = await _mediator.Send(new EmptyQuery());
return View();
}
Please bear in mind that query can return other types not necessarily the string.
I would like to create some kind of a bridge class e.g. MediatorBoostrapper, which allows me to run some business logic(e.g. log command/query via Logger) every time the Publish method is invoked and then
invoke the public Task Handle(EmptyCommand notification,... method from the command handler. The solution must be generic, so this method would be invoked every time I run the Publish method. I also want to be able to do the same thing for the Send method.
I was thinking about the creation of the public class MediatorBoostrapper : IMediator
but not sure what should be a proper implementation of the class and if my idea is good.
Any ideas? Cheers
Edit
I want to have an example of how to use the Behaviors
to create a generic way to run some external method from the generic handler every time I Run the Send method for queries. I want to have a similar example for Publish method, which I use for sending commands.
I want to have an example of how to use Polymorphic dispatch
for the creation of the GenericCommandHandler and a GenericQueryHandler
I created a sample project on GitHub which can be found here
You can feel free to try to extend this project with your solution.
This time I want to answer the question starting from the end.
2.
TL;DR Polymorphic Dispatch cannot be used for the CQS
After some time of playing with the MediatR library, reading the comments under my Question and consultation with my friend, I found the Polymorphic Dispatch(PD) can be used to create a generic handler only in case of the Commands. The PD solution cannot be implemented for Queries. Based on the Documentation, the handlers are contravariant and not covariant. This means the PD works only in the case where the TResponse is a constant type. In case of the Queries, this is false and each Query handler can return a different result.
I also found this issue. I think it's interesting to know you can use the Polymorphic Dispatch only if your container supports it.
1. Behaviors is the one and only solution for CQS when using the MediatR.
Based on the comment under my question from #Steve and comment from jbogard I've found the way how to use Behaviors and IRequestHandler for the strict Command pattern. The full comment:
Just to summarize the changes, there are 2 main flavors of requests:
those that return a value, and those that do not. The ones that do not
now implement IRequest<T> where T : Unit. This was to unify requests
and handlers into one single type. The diverging types broke the
pipeline for many containers, the unification means you can use
pipelines for any kind of request.
It forced me to add the Unit type in all cases, so I've added some helper classes for you.
IRequestHandler<T> - implement this and you will return Task<Unit>.
AsyncRequestHandler<T> - inherit this and you will return Task.
RequestHandler<T> - inherit this and you will return nothing (void).
For requests that do return values:
IRequestHandler<T, U> - you will return Task<U>
RequestHandler<T, U> - you will return U
I got rid of the AsyncRequestHandler because it really wasn't doing anything after the consolidation, a redundant base class.
The example
a) The Commands management:
public class EmptyCommand : IRequest{...}
public class EmptyCommandHandler : RequestHandler<EmptyCommand>
{
protected override void Handle(EmptyCommand request){...}
}
b) The Queries management:
// can be any other type not necessarily `string`
public class EmptyQuery : IRequest<string>{...}
public class EmptyQueryHandler : IRequestHandler<EmptyQuery, string>
{
public Task<string> Handle(EmptyQuery notification, CancellationToken cancellationToken)
{
return Task.FromResult("Sample response");
}
}
c) The sample LogginBehavior class:
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;
public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var requestType = typeof(TRequest).Name;
var response = await next();
if (requestType.EndsWith("Command"))
{
_logger.LogInformation($"Command Request: {request}");
}
else if (requestType.EndsWith("Query"))
{
_logger.LogInformation($"Query Request: {request}");
_logger.LogInformation($"Query Response: {response}");
}
else
{
throw new Exception("The request is not the Command or Query type");
}
return response;
}
}
d) To register the LoggingBehavior add the command
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
to the body of the ConfigureServices method in the Startup.cs.
e) The example of how to run sample command and query:
await _mediator.Send(new EmptyCommand());
var result = await _mediator.Send(new EmptyQuery());
MediatR supports dispatching notifications to generic handlers (polymorphic dispatch). For example:
public class GenericHandler<TNotification> : INotificationHandler<TNotification>
where TNotification : INotification
{
public Task Handle(TNotification notification, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
This handler will be invoked for every notification that is published through Publish(). The same is true for requests (queries/commands). You should also take a look at behaviors.
If you're using MediatR with ASP.NET Core I suggest you use the MediatR.Extensions.Microsoft.DependencyInjection library which takes care of wiring all the handlers together.

Simple Injector conditional registration based on a type

I am using MediatR to execute my requests. Inside the concrete handler my requirement is to execute, depending on the case, a command on one or more object instances.
Let's make a simple example. Suppose the request is to execute a topup for a mobile service customer. The requirement state that if the customer has more than one service activated, then the topup must be done on every service.
When the handler run it first load customer services
var services = context.Services.Where(c => c.CustomerId == customerId);
foreach ( var service in services ) {
//Do the topup
}
To handle topups I have implemented it with the Command pattern. So I have the following model
where the Invoker is exactly the MediatR handler instance. This is my receiver interface
internal interface IReceiver<TRequest, TResponse>
where TRequest : DefaultRequest
where TResponse : DefaultResponse
{
TResponse Apply( TRequest request );
bool SupportsCommand( Type commandType );
}
and there are some different implementations based on network elements brand (Nokia, Ericsson, etc.)
internal abstract class BaseNokiaReceiver<TRequest, TResponse>
: IReceiver<TRequest, TResponse>
where TRequest : DefaultRequest
where TResponse : DefaultResponse
{
public BaseNokiaReceiver( ILogger logger, DataContext context, Service service )
: base( logger, context, service ) {
}
public abstract TResponse Apply( TRequest request );
}
and a concrete one
internal class NokiaDataTrafficReceiver : BaseNokiaReceiver<TopupRequest, TopupResponse>
{
public NokiaDataTrafficReceiver(ILogger logger, DataContext context, Service service)
: base(logger, context, service) {
}
public override TopupResponse Apply( TopupRequest request ) {
//[...] Application code
}
}
Because of the fact that a customer can have more than one service enabled on its account, I necessarily need to have more than one receiver with a single command class like, for example
internal abstract class AbstractCommand<TRequest, TResponse>
: ICommand<TRequest, TResponse>
where TRequest : DefaultRequest
where TResponse : DefaultResponse
{
protected IReceiver<TRequest, TResponse> _receiver;
public AbstractCommand( IReceiver<TRequest, TResponse> receiver ) {
_receiver = receiver;
}
public abstract Task<TResponse> Apply( TRequest request );
}
and
internal class Reload : AbstractCommand<TopupRequest, TopupResponse>
{
public Reload( IReceiver<TopupRequest, TopupResponse> receiver ) : base( receiver ) {
}
public async override Task<TopupResponse> Apply( TopupRequest request ) {
var response = _receiver.Apply( request );
return await Task.FromResult<TopupResponse>( response );
}
}
The handler implementation then become something like the following
var services = context.Services.Where( c => c.CustomerId = customerId );
foreach ( var service in services ) {
IReceiver<TopupRequest, TopupResponse> receiver = null;
if ( service is VoiceService ) {
receiver = new VoiceAccountReceiver();
}
else if ( service is DataService ) {
receiver = new DataTrafficReceiver();
}
Reload command = new Reload( receiver );
var result = command.Apply( input );
}
And each receiver does a single specific topup.
Actually the instantiation of receivers happens in code and I would like to change it in a way that I can use the DI container.
By talking in pseudo code I would like to register an instance of a Receiver strictly tied to a Type, e.g.
container.Register<IReceiver<TopupRequest, TopupResponse>, VoiceAccountReceiver>()
.WhenParameterofType<VoiceService>();
container.Register<IReceiver<TopupRequest, TopupResponse>, DataTrafficReceiver>()
.WhenParameterofType<DataService>();
and so resolve the type at runtime with
container.GetInstance<IReceiver<TopupRequest, TopupResponse>>( typeof(service) );
As I see it, you are missing some type information that allows you to distinguish at compile-time between operations. This seems obvious, because you are adding type-checks on VoiceService and DataService.
So what you could try is adding that type information to the IReceiver<TRequest, TResponse> abstraction. For instance:
interface IReceiver<TRequest, TResponse, TService> { }
This way, VoiceAccountReceiver could be implemented as follows:
class VoiceAccountReceiver : IReceiver<TopupRequest, TopupResponse, VoiceService>
This allows the correct receiver be resolved based on its available type information. Preferably, your handler should take a dependency on sort of mediator abstraction. The mediator implementation will be responsible for calling back into the container to resolve the correct type.
For instance:
var services = context.Services.Where(c => c.CustomerId == customerId);
foreach ( var service in services ) {
var result = this.receiverMediator.Apply(input, service);
}
Your IReceiverMediator can be defined as follows:
interface IReceiverMediator
{
TResponse Apply<TRequest, TService>(TRequest input, object service)
where TRequest : IRequest<TResponse>;
}
And a Simple Injector specific implementation can be defined as follows:
class SimpleInjectorReceiverMediator : IReceiverMediator
{
private readonly Container container;
public SimpleInjectorReceiverMediator(Container container) {
this.container = container;
}
public TResponse Apply<TRequest, TService>(TRequest input, object service)
where TRequest : IRequest<TResponse>
{
Type receiverType =
typeof(IReceiver<,,>).MakeGenericType(typeof(TRequest), typeof(TResponse), service.GetType());
dynamic receiver = container.GetInstance(receiverType);
return (TResponse)receiver.Action();
}
}
This way there are no conditional registrations at all. You can simply batch-register all receivers as follows:
container.Register(typeof(IReceiver<,,>), assemblies);
container.Register<IReceiverMediator>(new SimpleInjectorReceiverMediator(container));

Mediatr 3.0 Using Pipeline behaviors for authentication

Looking at using the new Mediatr 3.0 feature pipeline behaviors for authentication/authorization.
Would you normally auth based on the message or the handler? reason I'm asking is that I'd auth on the handler (same as controller in MVC) but behaviors don't appear to have knowledge about the handler so I'm not sure this is possible/suitable.
I could add an IAuthorisationRequired marker interface to each message, but if the message is a notification/event and has multiple handlers then maybe some should run but not others. Really does feel better checking auth on the handler code that does the actual work.
Would love to be able to put a [Authorize] attribute on a handler and user a behaviour to check it (I currently do exactly this but with a base class instead of a behaviour).
public class AuthenticationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
public Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
{
//Can't access handler class here, so how do I know the action requires authentication/authorization?
return next();
}
}
[Authorize]
public class ChangePasswordRequestHandler : IAsyncRequestHandler<ChangePassword, ReponseType>
{
protected override async Task<ReponseType> Handle(AsyncRequestBase<ChangePassword> message)
{
//change users password here
}
}
You're right, the RequestDelegateHandler<TResponse> doesn't expose what handler will run next, and this is intentional. If you think about it, pipelines in MediatR 2.x used decorators, and while the decorator had access to the instance of the decoratee, I would advise against doing auth based on it. The reason is that if you need your authorization decorator to decorate one specific instance of a handler - the one decorated with specific attributes - then they're coupled, which defeats the purpose of decorators where you should be able to put them on top of each other independently.
That's why I would advise basing authorization on the message, at least in most cases. You could have an extensible design where to each message are associated several authorization rules, and a behavior evaluates all of them.
public interface IAuthorizationRule<TRequest>
{
Task Evaluate(TRequest message);
}
public class AuthorizationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly IAuthorizationRule<TRequest>[] _rules;
public AuthorizationBehavior(IAuthorizationRule<TRequest>[] rules)
{
_rules = rules;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
{
// catch it or let it bubble up depending on your strategy
await Task.WaitAll(_rules.Select(x => x.Evaluate(request)));
return next();
}
}
For the specific case you mention where, for a notification, some handlers might run while others shouldn't, you can always use authorization behaviors that target that specific message and apply them selectively to the handlers that need them. I guess my point is you'll have to do a bit of crafting yourself when you hit those specific scenarios.
I had the same requirement for a project and implemented a specific pipeline where I could inject (if required) a AuthorisationHandler for a specific request. This means I just need to add a new AuthorisationHandler for each new command that I created, and then it will be called before the request to process the actual command.
The pipeline:
public class Pipeline<TRequest, TResponse> : IAsyncRequestHandler<TRequest, TResponse> where TRequest : IAsyncRequest<TResponse>
{
private readonly IAuthorisationHandler<TRequest, TResponse>[] _authorisationHandlers;
private readonly IAsyncRequestHandler<TRequest, TResponse> _inner;
private readonly IPostRequestHandler<TRequest, TResponse>[] _postHandlers;
public Pipeline(IAuthorisationHandler<TRequest, TResponse>[] authorisationHandlers, IAsyncRequestHandler<TRequest, TResponse> inner, IPostRequestHandler<TRequest, TResponse>[] postHandlers)
{
_authorisationHandlers = authorisationHandlers;
_inner = inner;
_postHandlers = postHandlers;
}
public async Task<TResponse> Handle(TRequest message)
{
foreach (var authorisationHandler in _authorisationHandlers)
{
var result = (ICommandResult)await authorisationHandler.Handle(message);
if (result.IsFailure)
{
return (TResponse)result;
}
}
var response = await _inner.Handle(message);
foreach (var postHandler in _postHandlers)
{
postHandler.Handle(message, response);
}
return response;
}
}
The Authorsiation Handler:
public class DeleteTodoAuthorisationHandler : IAuthorisationHandler<DeleteTodoCommand, ICommandResult>
{
private IMediator _mediator;
private IAuthorizationService _authorisationService;
private IHttpContextAccessor _httpContextAccessor;
public DeleteTodoAuthorisationHandler(IMediator mediator, IAuthorizationService authorisationService, IHttpContextAccessor httpContextAccessor)
{
_mediator = mediator;
_authorisationService = authorisationService;
_httpContextAccessor = httpContextAccessor;
}
public async Task<ICommandResult> Handle(DeleteTodoCommand request)
{
if (await _authorisationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, "DeleteTodo"))
{
return new SuccessResult();
}
var message = "You do not have permission to delete a todo";
_mediator.Publish(new AuthorisationFailure(message));
return new FailureResult(message);
}
}
My AuthorisationHandler implemements IAuthorisationHandler which looks like this:
public interface IAuthorisationHandler<in TRequest, TResponse> where TRequest : IAsyncRequest<TResponse>
{
Task<TResponse> Handle(TRequest request);
}
It then hangs together using the DecorateAllWith (part of structuremap)
cfg.For(typeof(IAsyncRequestHandler<,>)).DecorateAllWith(typeof(Pipeline<,>));
Not sure you should do this for 3.x as this now has a new pipeline interface
IPipelineBehavior<TRequest, TResponse>
Not used it yet but I think it will simplify the implementation and mean you can stop using the decorator pattern DecorateAllWith.
You could do this in the same way I use Fluent Validation.
I created the following behaviour:
namespace MediatR.Extensions.FluentValidation
{
public class ValidationPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly IValidator<TRequest>[] _validators;
public ValidationPipelineBehavior(IValidator<TRequest>[] validators)
{
_validators = validators;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
{
var context = new ValidationContext(request);
var failures =
_validators.Select(v => v.Validate(context)).SelectMany(r => r.Errors).Where(f => f != null).ToList();
if (failures.Any())
{
throw new ValidationException(failures);
}
return await next();
}
}
}
Create a AbstractValidator
public classs SaveCommand: IRequest<int>
{
public string FirstName { get; set; }
public string Surname { get; set; }
}
public class SaveCommandValidator : AbstractValidator<SaveCommand>
{
public SaveCommandValidator()
{
RuleFor(x => x.FirstName).Length(0, 200);
RuleFor(x => x.Surname).NotEmpty().Length(0, 200);
}
}
So you could create a Authorization<T> class where you could add your custom authorization code per request and have injected into a AuthorizationPipelineBehavior<TRequest, TResponse> class.

Categories