Search code examples
.netasp.net-coredependency-injectionnservicebus

Scoped dependency use from NServiceBus Behavior


I'm trying to use a Scoped dependency from a NServiceBus Behavior.

From NServiceBus Behavior docs:

Behaviors are only created once and the same instance is reused on every invocation of the pipeline. Consequently, every behavior dependency will also behave as a singleton, even if a different option was specified when registering it in dependency injection. Furthermore, the behavior, and all dependencies called during the invocation phase, need to be concurrency safe and possibly stateless. Storing state in a behavior instance should be avoided since it will cause the state to be shared across all message handling sessions. This could lead to unwanted side effects.

Since a Behavior is a Singleton and the Invoke method of the Behavior doesn't allow to inject any dependency (such as the invoke method of a net core middleware because in this case it's a regular interface implementation), I can't use a scoped dependency from here.

I've tried to resolve my dependencies in my Invoke method for each incoming/outgoing message by passing IServiceCollection in the constructor:

private readonly IServiceCollection _services;

public MyIncomingMessageBehavior(IServiceCollection services)
{
    _services = services;
}

public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next)
{
    var myScopedDependency = _services.BuildServiceProvider().GetService<IMyScopedDependency>();
    // always 
}

But this doesn't work:

That's because when you inject IServiceProvider into your middleware - that's "global" provider, not request-scoped. There is no request when your middleware constructor is invoked (middleware is created once at startup), so it cannot be request-scoped container.

In summary, my scoped dependency contains data for the current context and I'd like to access this data from the Invoke method of my Behavior singleton?

Is there any way to do it?


Solution

  • You need to create a scope before resolving your dependency:

    private readonly IServiceScopeFactory _scopeFactory;
    
    public MyIncomingMessageBehavior(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }
    
    public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next)
    {
        using(var scope = _scopeFactory.CreateScope())
        {
            var myScopedDependency = scope.ServiceProvider.GetService<IMyScopedDependency>();
        }
    }
    

    Also, pay attention that your dependency is disposed along with scope.