SimpleInjector MediatR scope lost

166 Views Asked by At

Within a Blazor server application I do use the Simple Injector integration as well as the MediatR. The Simple Injector looks like it works pretty fine together with Blazor with respect to the scoping, but it breaks once it reaches the mediator and the scope is lost and recreated somehow.

Within the application I created a simple scenario to demonstrate the issue:

The simple idea is I have a scoped service that returns different GUID for every instance so I can track if the instance is still the same. I will invoke the service directly or via the MediatR.

public interface IScoped { public Guid Id { get; init; } }
public class TestScoped : IScoped {
    public TestScoped() => Id = Guid.NewGuid();
    public Guid Id { get; init; }
}
public interface IScopedCaller {void CallScoped(); }

public class ScopedCaller : IScopedCaller  {
    private readonly IMediator _mediator;
    private readonly IScoped _scoped;

    public ScopedCaller(IMediator mediator, IScoped scoped) {
        _mediator = mediator;_scoped = scoped;
    }
    public void CallScoped() {
        Debug.WriteLine(_scoped.Id);
        _mediator.Send(new TestScopedRequest());
    }
}
public class TestScopedRequest: IRequest{ }

public class TestScopedRequestHandler : IRequestHandler<TestScopedRequest> {
    private readonly IScoped _scoped;

    public TestScopedRequestHandler(IScoped scoped){
        _scoped = scoped;
    }

    public async Task<Unit> Handle(TestScopedRequest request, CancellationToken _){
        Debug.WriteLine(_scoped.Id);
        return default;
    }
}

If I reference the IScoped directly from the application/Blazor pages, I get a correct/expected output across multiple components (the GUID is the same):

GUIDs are the same

If I invoke the ScopedCaller.CallScoped() method, this is what I find in the console:

5a7c7871-15d7-4222-bc3f-77e8ab964031
0e10fe5a-8a3d-4b92-bd68-85e904eda187

The first (correct) line comes from the CallScoped() method directly. The other one - new GUID - comes from the Handle method.

It means the handler uses a different instance since it operates in a different scope.

All the registrations in the Simple Injector (including the Mediator itself and the RequestHandler) are Lifestyle.Scoped.

How can I achieve the same instance is used all the time within the same scope? Singleton would not work for me. I need to ensure that if I run a mediator request from inside a scope, the scope is reused within the mediator too.

1

There are 1 best solutions below

0
Jan Drozen On

I found what was the issue for this particular issue. I was able to reproduce it using a new template project. The problem was I had in my startup config:

services.AddSimpleInjector(container, options => {
    options.AddAspNetCore()
       .AddControllerActivation()
       .AddViewComponentActivation();
    options.AddServerSideBlazor(new[] {typeof(Program).Assembly});
})

The default behavior of the ASP.NET Core integration caused the issue with scopes - actually nested scoping.

I got rid of it by the flag:

 options.AddAspNetCore(ServiceScopeReuseBehavior.OnePerNestedScope)