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

AddOpenBehavior doesn't register pipeline behaviors with nested generic parameters #1051

Closed
Mitchman215 opened this issue Jul 23, 2024 · 5 comments
Labels

Comments

@Mitchman215
Copy link

Summary

It seems like AddOpenBehavior doesn't register the pipeline behavior PipelineBehaviorImpl<TRequest, TNested> where TRequest : IRequest<SomeType<TNested>>.

Simple Example

Here is an example to demonstrate the issue when using generic lists:

// Create a new service collection and add mediatr
var services = new ServiceCollection();
services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
    cfg.AddOpenBehavior(typeof(ListGenericPipelineBehavior<,>));
});

// Build the service provider
var serviceProvider = services.BuildServiceProvider();

using var scope = serviceProvider.CreateScope();
var mediatr = scope.ServiceProvider.GetRequiredService<IMediator>();
await mediatr.Send(new ReturnListRequest());


internal record ReturnListRequest : IRequest<List<string>>;

class ReturnListRequestHandler : IRequestHandler<ReturnListRequest, List<string>>
{
    public Task<List<string>> Handle(ReturnListRequest request, CancellationToken cancellationToken)
    {
        Console.WriteLine("Generic LIST request executed!!");
        List<string> list = ["val"];
        return Task.FromResult(list);
    }
}

internal class ListGenericPipelineBehavior<TRequest, TNested> : IPipelineBehavior<TRequest, List<TNested>>
    where TRequest : IRequest<List<TNested>>
{
    public async Task<List<TNested>> Handle(TRequest request, RequestHandlerDelegate<List<TNested>> next,
        CancellationToken cancellationToken)
    {
        Console.WriteLine($"{this.GetType()}  Before");
        
        var response = await next();
        Console.WriteLine($"{this.GetType()}  After");
        return response;
    }
}

Running the above code results in the following output:

Generic LIST request executed!!

This means the ListGenericPipelineBehavior was not run since the Before and After messages did not print.

More complicated example

This issue also happens with other types beyond List<T>. My original use case was for pipelines that would apply to some abstract generic base class like BaseCommand<T> below:

// Create a new service collection and add mediatr
var services = new ServiceCollection();
services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
    cfg.AddOpenBehavior(typeof(CommandPipelineBehavior<,>));
});

// Build the service provider
var serviceProvider = services.BuildServiceProvider();

using var scope = serviceProvider.CreateScope();
var mediatr = scope.ServiceProvider.GetRequiredService<IMediator>();
await mediatr.Send(new StringCommand());


internal record Result<T>(T Value);

internal abstract record BaseCommand<TElement> : IRequest<Result<TElement>>
{
    public Guid Id { get; init; } = Guid.NewGuid();
}

internal record StringCommand : BaseCommand<string>;

internal class StringCommandHandler : IRequestHandler<StringCommand, Result<string>>
{
    public Task<Result<string>> Handle(StringCommand request, CancellationToken cancellationToken)
    {
        Console.WriteLine("String Command executed!!");
        return Task.FromResult(new Result<string>("hey"));
    }
}

internal class CommandPipelineBehavior<TCommand, TElement> : IPipelineBehavior<TCommand, Result<TElement>>
    where TCommand : BaseCommand<TElement>
{
    public async Task<Result<TElement>> Handle(TCommand request, RequestHandlerDelegate<Result<TElement>> next,
        CancellationToken cancellationToken)
    {
        Console.WriteLine($"{this.GetType()}  Before, executing command {request.Id}");
        
        var response = await next();
        // Some more logic based on Result...
        Console.WriteLine($"{this.GetType()}  After, executed command {request.Id}");
        return response;
    }
}

The only output from running the above example is

String Command executed!!

showing that CommandPipelineBehavior didn't run.

Note: while testing this, I noticed that removing the Result<T> type entirely (ie making it so BaseCommand implements IRequest<TElement> instead) resulted in the pipeline behavior being successfully called, so the problem seems to specifically be when the Request's return type has some nested generic parameter itself.

Workaround

Explicitly registering the behaviors with AddBehavior<ListGenericPipelineBehavior<ReturnListRequest, string>>() and AddBehavior<CommandPipelineBehavior<StringCommand, string>>() fixes the issue. This leads me to believe that the root of the problem is with AddOpenBehavior() specifically and not in how the pipelines are looked up and run.

Misc info

The above examples were using MediatR 12.3.0, Microsoft.Extensions.DependencyInjection 8.0.0, and targeting .NET 8 in a Windows environment.

If this is already a known limitation / feature that isn't meant to be implemented, then it'd be nice to document it in the Wiki

@Mitchman215 Mitchman215 changed the title AddOpenBehavior doesn't register pipelines behaviors with nested generic parameters AddOpenBehavior doesn't register pipeline behaviors with nested generic parameters Jul 23, 2024
@sakukk
Copy link

sakukk commented Aug 13, 2024

I have the same issue

Copy link

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions github-actions bot added the Stale label Oct 12, 2024
@Mitchman215
Copy link
Author

Not stale, this is still a problem as far as I am aware

@github-actions github-actions bot removed the Stale label Oct 18, 2024
Copy link

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions github-actions bot added the Stale label Dec 17, 2024
Copy link

github-actions bot commented Jan 1, 2025

This issue was closed because it has been stalled for 14 days with no activity.

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Jan 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants