modular-monolith-with-ddd icon indicating copy to clipboard operation
modular-monolith-with-ddd copied to clipboard

UnitOfWorkCommandHandlerDecorator in UserAccess Module Doesn't Get Called.

Open kacey90 opened this issue 4 years ago • 8 comments

Hello Kamil,

I thought I have figured the project architecture from end to end and decided to try it out on a side project. I am almost sure I followed your example thoroughly and didn't miss out anything. But when I tested it by trying out the RegisterUser endpoint, It returns 200 but the user isn't found at least in the database. I tried to step through the process and realized that all through the process of the step-through, UnitOfWorkCommandHandlerDecorator and ValidationCommandHandlerDecorator were never called. When this line from CommandsExecutor.cs runs - return await mediator.Send(command); , Dispose() of SqlConnectionFactory.cs gets called next. I don't know why this happens.. At some point, the debug was stuck in between Quartz and SerilogLogProvider. I don't know what I'm missing, and I've been on this for days. I was hoping you would point me to the right direction because my frustration is building up. Please help.

kacey90 avatar May 26 '20 11:05 kacey90

Hello @kgrzybek I am still struggling with this challenge. I am in dire need of some assistance.

kacey90 avatar May 29 '20 23:05 kacey90

Hi @kacey90

I checked my solution and it works - you can check this by running integration test: RegisterNewUserCommand_Test

I don't see your code so it is hard to give you an answer but I can guess that your decorators are not registered correctly. Remember, that if you have

  • commands without result (inherits from CommandBase)
  • commands with the result (inherits from CommandBase<TResult>)

you must create and register two sets of decorators for each of them. Maybe this is the issue?

In fact, I wonder whether to give up the command without result in the future and always return some kind of result for all commands.

kgrzybek avatar May 30 '20 19:05 kgrzybek

Hi @kgrzybek ,

Thanks for your response. I have both decorators created and registered. But I am currently doing comparative checks with your code base to see if I'm missing something. My last resort may be to make a fork of your project and refactor it to my specifications.

Based on your suggestion on the command decorators, we may ditch the one without the "Result" since it's mostly unused throughout the project.

kacey90 avatar May 30 '20 19:05 kacey90

@kacey90 I had a similar problem with the decorator and in my situation, there are a couple of problems.

The first problem is related to missing registrations of command handlers which are implements ICommandHandler<>. Thereby, MediatR resolved the IRequestHandler<> service from the container and my handlers were skipped. Another problem was in registering the decorator itself. You no need to set serviceType as typeof(ICommandHandler<>), just change it to the typeof(IRequestHandler<>). e.g. builder.RegisterGenericDecorator(typeof(UnitOfWorkCommandHandlerDecorator<>), typeof(IRequestHandler<>)); That new behavior with decorator registering was introduced in the Autofac starting from the v5. Kamil's infrastructure project uses Autofac v4.9.2 where we have no such problems in registration. See details here

cc @kgrzybek FYI

dbeylkhanov avatar Oct 24 '20 08:10 dbeylkhanov

This work with Autofac v6.1 See details builder.RegisterGenericDecorator( typeof(UnitOfWorkCommandHandlerWithResultDecorator<,>), typeof(ICommandHandler<,>), context => !context.AppliedDecorators.Any());

AspDotNetCP avatar Jan 11 '21 16:01 AspDotNetCP

Hi, I'm trying to decorate IRequestHandler <TCommand>, but it doesn't work (without result). The LoggingDecorator and DiagnosticDecorator decorators are never invoked

I have no problem decorating IRequestHandler <TCommand, TResult> (with result)

TargetFramework net5.0 Autofac 6.1.0 MediatR 9.0.0 MediatR.Extensions.Autofac 0.6.0 MediatR.Extensions.Autofac.DependencyInjection 7.1.0

` class Program { static IContainer container;

    static async Task Main(string[] args)
    {

        await Setup();


        using (var scope = container.BeginLifetimeScope())
        {
            var mediatr = scope.Resolve<IMediator>();
            await mediatr.Send(new CreatePersonCommand() { x = 1, y = 2 });
        }
    }


    public static async Task Setup()
    {
        var builder = new ContainerBuilder();

        builder.RegisterMediatR(typeof(Program).Assembly);
        builder.RegisterGenericDecorator(typeof(DiagnosticDecorator<>), typeof(IRequestHandler<>));
        builder.RegisterGenericDecorator(typeof(LoggingDecorator<>), typeof(IRequestHandler<>));
        container = builder.Build();
    }
}

public class LoggingDecorator<TCommand> : IRequestHandler<TCommand>
   where TCommand : IRequest
{

    private readonly IRequestHandler<TCommand> commandHandler;

    public LoggingDecorator(IRequestHandler<TCommand> commandHandler)
    {
        this.commandHandler = commandHandler;
    }
    public async Task<Unit> Handle(TCommand request, CancellationToken cancellationToken)
    {
        Console.WriteLine("executing...");
        return await this.commandHandler.Handle(request, cancellationToken);
    }
}

public class DiagnosticDecorator<TCommand> : IRequestHandler<TCommand>
    where TCommand : IRequest
{

    private readonly IRequestHandler<TCommand> commandHandler;

    public DiagnosticDecorator(IRequestHandler<TCommand> commandHandler)
    {
        this.commandHandler = commandHandler;
    }
    public async Task<Unit> Handle(TCommand request, CancellationToken cancellationToken)
    {

        var r = await commandHandler.Handle(request, cancellationToken);

        return r;
    }
}


public class CreatePersonCommand : IRequest
{
    public int x { get; set; }

    public int y { get; set; }
}

public class CreatePersonCommandHandler : IRequestHandler<CreatePersonCommand>
{
    public async Task<Unit> Handle(CreatePersonCommand request, CancellationToken cancellationToken)
    {
        return Unit.Value;
    }
}

`

also try registering the decorators like this, but it doesn't work

builder.RegisterGenericDecorator(typeof(DiagnosticDecorator<>), typeof(IRequestHandler<>), context=> !context.AppliedDecorators.Any()); builder.RegisterGenericDecorator(typeof(LoggingDecorator<>), typeof(IRequestHandler<>), context =>!context.AppliedDecorators.Any());

thanks for your help

ghuarcayam avatar Apr 25 '21 00:04 ghuarcayam

@ghuarcayam downgrade autofac version thesame with project currenly using.

arvylagunamayor avatar May 12 '21 07:05 arvylagunamayor

I've managed to do it like this (Autofac 6.2.0):

builder.RegisterGenericDecorator(
    typeof(UnitOfWorkCommandHandlerDecorator<>),
    typeof(IRequestHandler<>),
    context => context.ImplementationType.FullName.EndsWith("CommandHandler"));

builder.RegisterGenericDecorator(
    typeof(UnitOfWorkCommandHandlerWithResultDecorator<,>),
    typeof(IRequestHandler<,>),
    context => context.ImplementationType.FullName.EndsWith("CommandHandler"));

andreyppsc avatar Aug 02 '21 13:08 andreyppsc