10

I've just upgraded a .net core app from version 2.2 to 3. Inside the ConfigureServices method in startup.cs I need to resolve a service that is used by the authentication service. I was "building" all the services using "services.BuildServiceProvider()" but .net core 3 complains about the method creating additional copies of the services and suggesting me to dependency injecting services as parameters to 'configure'. I have no idea what the suggestion means and I'd like to understand it.

public virtual void ConfigureServices(IServiceCollection services)
{
    // Need to resolve this.
    services.AddSingleton<IManageJwtAuthentication, JwtAuthenticationManager>();

    var sp = services.BuildServiceProvider(); // COMPLAINING HERE!!
    var jwtAuthManager = sp.GetRequiredService<IManageJwtAuthentication>();

    services
        .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(c =>
        {
            c.TokenValidationParameters = new TokenValidationParameters
            {
                AudienceValidator = jwtAuthManager.AudienceValidator,
                // More code here...
            };
        }
}

1 Answer 1

13

but .net core 3 complains about the method creating additional copies of the services and suggesting me to dependency injecting services as parameters to 'configure'.

Actually, the ServiceCollection.BuildServiceProvider() should be invoked by the Host automatically. Your code services.BuildServiceProvider(); will create a duplicated service provider that is different the default one, which might lead to inconsistent service states. See a bug caused by multiple Service Provider here.

To solve this question, configure the options with dependency injection instead of creating a service provider and then locating a service.

For your codes, rewrite them as below:

services.AddSingleton<IManageJwtAuthentication, JwtAuthenticationManager>();

services.AddOptions<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme)
    .Configure<IManageJwtAuthentication>((opts,jwtAuthManager)=>{
        opts.TokenValidationParameters = new TokenValidationParameters
        {
            AudienceValidator = jwtAuthManager.AudienceValidator,
            // More code here...
        };
    });

services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer();
Sign up to request clarification or add additional context in comments.

5 Comments

So basically when it "runs" the Authentication service it injects the jwtAuthManager service into the options?
@LucaMarangon There're will be a singleton AuthenticationSchemeOptions(e.g. JwtBearerOptions) for particular authentication handler(e.g. JwtBearerHandler). We can configure it by AddScheme<>(action) (e.g. AddJwtBearer(action)) , or configure the options by a options builder so that we can inject the service without building the service provider. In that case, your jwtAuthManager service is invoked whenever your authentication service invoked.
@itminus I have tested this code, and from what I see, jwtAuthManager gets called only on startup.
@Marko The answer was written almost 6 yrs ago and I'm not sure whether it is still correct now. However, I guess there's something misunderstanding : my comment above "your jwtAuthManager service is invoked whenever your authentication service invoked" says it is invoked whenever the Authentication Serive invoked instead of saying it is constructed everytime. If you believe it is not that case, you can create a minimal repo that reproduces and I'll look into that issue later.
Does this work with scoped dependencies, and what changes would be needed to make it work?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.