Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
225 views
in Technique[技术] by (71.8m points)

c# - Get a service in a IServiceCollection extension

I have this extension

public static class ServiceCollectionExtensions
{
    public static IServiceCollection MyExtension(this IServiceCollection serviceCollection)
    {
      ...
    }
}

and I need to get information from a service like this:

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        var myService = <<HERE>>();
        options.TokenValidationParameters = this.GetTokenValidationParameters(myService);
    });

how can I do that?

I tried to get the ServiceProvider after var serviceProvider = services.BuildServiceProvider(); and then I send the serviceProvider, but this doesn't work..

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

At the time you are calling services.AddSomething(), the service provider has not been built from the service collection yet. So you cannot instantiate a service at that time. Fortunately, there is a way to configure services while using dependency injection.

When you do services.AddSomething(options => …) what usually happens is that a certain amount of services will be registered with the service collection. And then the passed configuration action will also be registered in a special way, so that when the service is later instantiated, it will be able to execute that configuration action in order to apply the configuration.

For that, you need to implement IConfigureOptions<TOptions> (or actually IConfigureNamedOptions<TOptions> for authentication options) and register it as a singleton. For your purpose, this could look like this:

public class ConfigureJwtBearerOptions : IConfigureNamedOptions<JwtBearerOptions>
{
    private readonly IMyService _myService;

    public ConfigureJwtBearerOptions(IMyService myService)
    {
        // ConfigureJwtBearerOptionsis constructed from DI, so we can inject anything here
        _myService = myService;
    }

    public void Configure(string name, JwtBearerOptions options)
    {
        // check that we are currently configuring the options for the correct scheme
        if (name == JwtBearerDefaults.AuthenticationScheme)
        {
            options.TokenValidationParameters = myService.GetTokenValidationParameters();
        }
    }

    public void Configure(JwtBearerOptions options)
    {
        // default case: no scheme name was specified
        Configure(string.Empty, options);
    }
}

You then register that type in your Startup:

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    // add JwtBearer but no need to pass options here
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, configureOptions: null);

// instead we are registering our configuration type to configure it later
services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureJwtBearerOptions>();

This is actually the exact same thing that happens when you just do services.AddJwtBearer(scheme, options => { … }), just abstracted away, so you don’t need to care about it. But by doing it manually, you now have more power and access to the full dependency injection service provider.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...