Skip to content

Instantly share code, notes, and snippets.

@ericsampson
Last active November 15, 2020 19:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericsampson/bb37bd6da0748af2e928ee750563452a to your computer and use it in GitHub Desktop.
Save ericsampson/bb37bd6da0748af2e928ee750563452a to your computer and use it in GitHub Desktop.
Configuration pattern and ConfigureServices
// I have a service that needs to be initialized with a FooOptions and then added to the DI container.
// I also want to have IOptions<FooOptions> in the DI container to allow injection in other methods.
// How should I write my AddFoo extension method to support this?
public void ConfigureServices(IServiceCollection services)
{
services.AddFoo(fooOptions =>
{
configuration.GetSection("FooOptions").Bind(fooOptions)
fooOptions.param = "override";
});
}
namespace Microsoft.Extensions.DependencyInjection
{
public static class MyFooExtensions
{
public static IServiceCollection AddFoo(this IServiceCollection services,
Action<FooOptions> setupOptions)
{
services.Configure<FooOptions>(setupOptions);
services.AddSingleton<IFoo>(sp =>
{
var options = sp.GetRequiredService<IOptions<FooOptions>>();
return new FooFactory(options.Value);
};
return services;
}
}
}
@andrewlock
Copy link

Given that AddFoo() is just calling services.Configure<FooOptions>(options), then you can just ignore the configuration:

e.g.:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<FooOptions>(configuration.GetSection("FooOptions"));
    services.Configure<FooOptions>(options => { options.param = "override" });

    services.AddFoo(_ => {});
}

@ericsampson
Copy link
Author

@andrewlock, I just updated the gist to add line 31 - the motivation for my original question is that an instance of FooOptions is needed inside AddFoo, so I don't think that's going to work.
Sorry if that wasn't clear :) This just seems like a common scenario, so I must be missing something...

@andrewlock
Copy link

Yeah, unfortunately, you're kinda screwed in that case 🙁

Basically, requiring concrete IOptions<> in the AddFoo() method is not the recommended pattern, for exactly this reason! But if that's what it is, there's not a lot you can do...

In the few cases I've run into this I have just manually bound the configuration instead. It's not ideal, and means you can't use the full options pattern, but it may do:

public void ConfigureServices(IServiceCollection services)
{
    var fooOptions = new FooOptions();
    Configuration.Bind("FooOptions", fooOptions);

    services.AddFoo(fooOptions);
}

Rich Strahl has a nice post about this here: https://weblog.west-wind.com/posts/2017/dec/12/easy-configuration-binding-in-aspnet-core-revisited

@ericsampson
Copy link
Author

ericsampson commented Oct 25, 2020

@andrewlock, argh I'm an idiot. The signature is FooFactory(FooOptions options), not an IOptions<FooOptions>, if that helps.

I think I've revised the AddFoo() code in a way that should work now?? Or not...
I found a helpful blog post from @TsuyoshiUshio that gave me the idea:
https://medium.com/@tsuyoshiushio/servicecollection-di-with-optional-settings-fc4ee46984d1

@andrewlock
Copy link

@ericsampson That looks very similar to my suggestion, with a slight tweak. I don't think your suggestion will quite work though. Try this instead:

public void ConfigureServices(IServiceCollection services)
{
    services.AddFoo(fooOptions => 
    {
        configuration.GetSection("FooOptions").Bind(fooOptions)
        fooOptions.param = "override";
    });
}

@ericsampson
Copy link
Author

Thanks again @andrewlock, I'll give this all a go. Really appreciate the help :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment