Search code examples
asp.net-coredependency-injectionasp.net-core-identity

Dependency Injection Access While Configuring Service Registrations in asp.net Core (3+)


I have cases, where I want to configure services based on objects which are registered in the dependency injection container. For example I have the following registration for WS Federation:

authenticationBuilder.AddWsFederation((options) =>{                
       options.MetadataAddress = "...";
       options.Wtrealm = "...";                
       options.[...]=...
});

My goal in the above case is to use a configuration object, which is available via the DI container to configure the WsFederation-middleware. It looks to me that IPostConfigureOptions<> is the way to go, but until now, I have not found a way to accomplish this.
How can this be done, or is it not possible?


Solution

  • See https://andrewlock.net/simplifying-dependency-injection-for-iconfigureoptions-with-the-configureoptions-helper/ for the I(Post)ConfigureOptions<T> way, but I find that way too cumbersome.

    I generally use this pattern:

    // Get my custom config section
    var fooSettingsSection = configuration.GetSection("Foo");
    
    // Parse it to my custom section's settings class
    var fooSettings = fooSettingsSection.Get<FooSettings>() 
        ?? throw new ArgumentException("Foo not configured");
    
    // Register it for services who ask for an IOptions<FooSettings>
    services.Configure<FooSettings>(fooSettings);
    
    // Use the settings instance
    services.AddSomeOtherService(options => {
        ServiceFoo = fooSettings.ServiceFoo;
    })
    

    A little more explicit, but you have all your configuration and DI code in one place.

    Of course this bypasses the I(Post)ConfigureOptions<T> entirely, so if there's other code that uses those interfaces to modify the FooSettings afterwards, my code won't notice it as it's reading directly from the configuration file. Given I control FooSettings and its users, that's no problem for me.


    This should be the approach if you do want to use that interface:

    First, register your custom config section that you want to pull the settings from:

    var fooSettingsSection = configuration.GetSection("Foo");
    services.Configure<FooSettings>(fooSettingsSection);
    

    Then, create an options configurer:

    public class ConfigureWSFedFromFooSettingsOptions 
        : IPostConfigureOptions<Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions>
    {
        private readonly FooSettings _fooSettings;
    
        public ConfigureWSFedFromFooSettingsOptions(IOptions<FooSettings> fooSettings)
        {
            _fooSettings = fooSettings.Value;
        }
    
        public void Configure(WsFederationOptions options)
        {
            options.MetadataAddress = _fooSettings.WsFedMetadataAddress;
            options.Wtrealm = _fooSettings.WsFedWtRealm;
        }
    }
    

    And finally link the stuff together:

    services.AddTransient<IPostConfigureOptions<WsFederationOptions>, ConfigureWSFedFromFooSettingsOptions>();
    

    The configurer will get your IOptions<FooSettings> injected, instantiated from the appsettings, and then be used to further configure the WsFederationOptions.