Search code examples
c#.netasp.net-coredependency-injectionmiddleware

How can I modify a middleware or its registration to correctly use a scoped service?


I'm developing a multi-tenant application in .NET and I've run into an issue where I need to access a scoped service, ITenantProvider, from within a custom middleware, TenantMiddleware. However, when I try to run my application, I receive the following error:

Cannot resolve scoped service 'MyNameSpace.ITenantProvider' from root provider.

Here is the middleware:

public class TenantMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ITenantProvider _tenantProvider;

    public TenantMiddleware(ITenantProvider tenantProvider, RequestDelegate next)
    {
        _tenantProvider = tenantProvider;
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        // Logic to set tenant ID
        await _next(context);
    }
}

Startup config:

services.AddScoped<ITenantProvider, TenantProviderImplementation>();
builder.Services.AddTransient<IDBService, DBService>();
app.UseMiddleware<TenantMiddleware>();

The goal of the tenant ITenantProvider is to be set with a tenant Id (as a scoped) and the to be consumed by other services while it is injected as scoped to a transient db services.

public interface ITenantProvider
{
    public Guid GetTenantId();
    public void SetTenantId(Guid tenantId);
}

The DBService has the tenantId provider as a dependancy:

DbService(ITenantProvider tenant) { ... }

The most imprtant thing that the method DbLogic will be able to get from the injected ITenantProvider the tenantId via the method GetTenantId() that is correspondent with the one-time-called method (via the middleware) of SetTenantId(Guid tenantId)

DbLogic()
{
    Guid tenantId = _tenantProvider.GetTenantId();
}

I understand that the issue is related to trying to inject a scoped service (ITenantProvider) into a middleware that is a singleton, but I'm not sure how to correctly structure this to avoid the error.


Solution

  • You can resolve it from the context in the Invoke call (remove the ctor parameter):

    public async Task Invoke(HttpContext context)
    {
        var tenantProvider = context.RequestServices.GetRequiredService<ITenantProvider>();
        // Logic to set tenant ID
        await _next(context);
    }
    

    Or inject it directly to the method parameters:

    public async Task Invoke(HttpContext context, ITenantProvider provider)
    {
        // Logic to set tenant ID
        await _next(context);
    }