Handlers trigger multiple times for the same notification, but Auto Wiring works as expected

1.4k views Asked by At

I have ASP.NET Core 3.1 with StructureMap and Mediatr.

Previously, when I used default DI container, I would have my child handler trigger twice. Functionality is pretty limited there, so I switched to StructureMap. But still, when I apply different settings to scanner, I can't get expected behaviour. It is either base or child handler executed twice, or both. The only working case, is that I don't specify anything for typeof(INotificationHandler<>), everything works fine due to Auto Wiring (probably?), but I feel like there is delay between notification handler invokations, so I'd like to configure container correctly on application startup.

My notifications and notification handlers are below. Expected behaviour: when child notification is published, trigger both handlers once

public class ApplicationUserRegisteredNotification : INotification
{
    public ApplicationUser User { get; }

    public ApplicationUserRegisteredNotification(ApplicationUser user)
    {
        User = user;
    }
}

public class SpecificUserRegisteredNotification : ApplicationUserRegisteredNotification
{
    public SpecificUserProfile SpecificUser { get; }

    public SpecificUserRegisteredNotification(SpecificUserProfile specificUser)
        : base(specificUser.User)
    {
        SpecificUser = specificUser;
    }
}

public class SendEmailConfirmationEmail : INotificationHandler<ApplicationUserRegisteredNotification>
{
    // some code
}
public class SendAdminVerifySpecificUserRegistration : INotificationHandler<SpecificUserRegisteredNotification>
{
    // some code
}

I use StructureMap.AspNetCore to produce IServiceProvider, what option I could use to apply Auto Wiring logic explicitly? In the comments below you can see output for each option.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    // some code
    var container = new Container(cfg =>
    {
        cfg.Scan(scanner =>
        {
            scanner.AssemblyContainingType(GetType());
            scanner.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<>));
            scanner.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<,>));
            //scanner.ConnectImplementationsToTypesClosing(typeof(INotificationHandler<>)); // both handlers triggered twice in given sequence: base, child, base, child
            //scanner.AddAllTypesOf(typeof(INotificationHandler<>)); // both handlers triggered in given sequence: base, base, child
            // nothing: everything works as expected - base, child, but switching between handlers is rather slow
        });
    });

    container.Populate(services);

    return container.GetInstance<IServiceProvider>();
}

Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStructureMap()
            .UseStartup<Startup>();
}

Before StructureMap my configuration looked like that

services.AddTransient<INotificationHandler<SpecificUserRegisteredNotification>, SendEmailConfirmationEmail>();               
    services.AddTransient<INotificationHandler<OtherSpecificUserRegisteredNotification>, SendEmailConfirmationEmail>();
    services.AddTransient<IRequestHandler<RegisterSpecificCommand, BaseCommandResponse>, RegisterApplicationUserCommandHandler>();
    services.AddTransient<IRequestHandler<RegisterOtherSpecificCommand, BaseCommandResponse>, RegisterApplicationUserCommandHandler>();
1

There are 1 answers

0
Jeferson Moraes On

use:

services.AddMediatR(AppDomain.CurrentDomain.Load("AssemblyName"));

register your handlers normally. Example:

services.AddTransient<INotificationHandler<SpecificUserRegisteredNotification>, SendEmailConfirmationEmail>();