Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decorating command handlers #179

Open
fmontazeri opened this issue Jul 18, 2022 · 1 comment
Open

Decorating command handlers #179

fmontazeri opened this issue Jul 18, 2022 · 1 comment

Comments

@fmontazeri
Copy link

Hi dear,
I want to decorate command handlers with decorator Pattern in an ASP.NET Core app. So I registered command handers first and then register decorators.
But it doesn't work. I am using built-in IoC in ASP.NET Core and Scrutor.
You can see them below.

  public interface IMediatRCommandHandler<in TCommand, TResult> : IRequestHandler<TCommand, TResult>
     where TCommand : IMediatRCommand<TResult>
   {
   }
    
 public class TestCommandHandler : IMediatRCommandHandler<TestCommand, TestResult>
     {
         //implementation
    
     }
 public class TransactionalCommandHandlerDecorator<TCommand, TResult> : IMediatRCommandHandler<TCommand, TResult> , IHandlerDecorator
         where TCommand : IMediatRCommand<TResult>
     {
              private readonly IMediatRCommandHandler<TCommand, TResult> _decoratee;
    
             // implementation
     }

Registration of command handlers and decorators

 services.Scan(s => s.FromAssemblies(typeof(TestDecoratorCommand).Assembly)
                 .AddClasses(c =>c.Where(i => !typeof(IHandlerDecorator).IsAssignableFrom(i)).AssignableToAny(typeof(IMediatRCommandHandler<>), typeof(IMediatRCommandHandler<,>)))
                 .AsImplementedInterfaces()
                 .WithScopedLifetime());
                
  services.Decorate(typeof(IMediatRCommandHandler<,>), typeof(TransactionalCommandHandlerDecorator<,>)); // This line doesn't work

Could you tell me how can I do that , please?
Thanks in advance

@jhenriquez
Copy link

This is an old post but reading it made me realize I was not filtering out my decorators at registration time, which I think is a good idea. Bellow, I outlined what worked for me.

public interface ICommandHandler<in TCommand, TResponse> where TCommand : ICommand where TResponse: class
{
    Task<Result<TResponse>> Handle(TCommand command);
}
// Decorators

public class CommandExceptionHandlingBehavior<TCommand, TResult> : ICommandHandler<TCommand, TResult> where TCommand : ICommand where TResult : class
{
    private ICommandHandler<TCommand, TResult> NextCommandHandler { get; }
    
    public CommandExceptionHandlingBehavior(ICommandHandler<TCommand, TResult> nextCommandHandler)
    {
        NextCommandHandler = nextCommandHandler;
    }
    
    public async Task<Result<TResult>> Handle(TCommand command)
    {
        try
        {
            return await NextCommandHandler.Handle(command);
        }
        catch (QuotaExceededException ex)
        {
            return Result<TResult>.Failure(ex.AsError());
        }
        catch (Exception ex)
        {
            return Result<TResult>.Failure(new Error
            {
                Message = ex.Message,
                Reference = NextCommandHandler.GetType().ToString(),
                Type = Error.ErrorTypes.UnknownError
            });
        }
    }
}

public class CommandValidationBehavior<TCommand, TResult> : ICommandHandler<TCommand, TResult> where TCommand : ICommand where TResult : class
{
    private ICommandHandler<TCommand, TResult> NextCommandHandler { get; }
    private IValidator<TCommand> Validator { get; }

    public CommandValidationBehavior(ICommandHandler<TCommand, TResult> nextCommandHandler, IValidator<TCommand> validator)
    {
        NextCommandHandler = nextCommandHandler;
        Validator = validator;
    }
    
    public async Task<Result<TResult>> Handle(TCommand command)
    {
        var errors = await Validator.ValidateAsync(command);

        if (errors.Any())
        {
            return Result<TResult>.Failure(errors);
        }

        return await NextCommandHandler.Handle(command);
    }
}
// IoC Configurations

services.Scan(scan => scan.FromCallingAssembly()
           // First gather all the possible implementations of my commands and then filter out the decorators
            .AddClasses(classes => classes.AssignableTo(typeof(ICommandHandler<,>)).Where(c => !c.IsAssignableTo(typeof(ICommandBehavior))))
                .AsImplementedInterfaces()
                .WithTransientLifetime()
);

services.Decorate(typeof(ICommandHandler<,>), typeof(CommandValidationBehavior<,>));

services.Decorate(typeof(ICommandHandler<,>), typeof(CommandExceptionHandlingBehavior<,>));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants