15

What is the equivalent to the method Configure<TOptions> of the OptionsConfigurationServiceCollectionExtensions when using Autofac modules?

My ConfigureServices method looks like this, but I want to move the services.Configure<MyOptions>(Configuration.GetSection("MyOptions")) to MyModule.

public IServiceProvider ConfigureServices(IServiceCollection services) {
    services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));

    var containerBuilder = new ContainerBuilder();
    containerBuilder.Populate(services);
    containerBuilder.RegisterModule<MyModule>();

    var container = containerBuilder.Build();
    return new AutofacServiceProvider(container);
}

How does the registration look like in the Load-method of the Module

protected override void Load(ContainerBuilder builder)
{
    // configure options here
}
Jehof
  • 34,674
  • 10
  • 123
  • 155

3 Answers3

11

I'm not familiar with Autofac personally, but generally speaking, all Configure<T> does is 1) bind a particular configuration section to a class and 2) register that class with the service collection, so it can be injected directly.

As a result, you can instead use the following to bind your strongly-typed configuration:

var config = config.GetSection("MyOptions").Get<MyOptions>();

And, then you'd simply register that with Autofac as a constant in singleton-scope.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • 7
    You are correct: `builder.Register(p => configuration.GetSection("MyOptions").Get()).SingleInstance();` –  Oct 16 '18 at 13:16
10

I recently encountered this same issue, I implemented the following so that you can still use IOptions, IOptionsMonitor and IOptionsSnapshot, but register the configuration from the AutoFac Module.

The prerequisite is that you call services.AddOptions() in ConfigureServices method:

var sfConfig = _configuration.GetSection("MyOptions");

builder.Register(ctx => new ConfigurationChangeTokenSource<MyOptions>(Options.DefaultName, sfConfig))
       .As<IOptionsChangeTokenSource<MyOptions>>()
       .SingleInstance();

builder.Register(ctx => new NamedConfigureFromConfigurationOptions<MyOptions>(Options.DefaultName, sfConfig, _ => { }))
       .As<IConfigureOptions<MyOptions>>()
       .SingleInstance();

This requires that you run services.AddOptions() within the ConfigureServices method.

In the example above, "MyOptions" is the section name in your configuration, and MyOptions type is the POCO class that has the fields to hold the result.

This is basically a conversion of what microsoft has here: https://github.com/aspnet/Options/blob/master/src/Microsoft.Extensions.Options.ConfigurationExtensions/OptionsConfigurationServiceCollectionExtensions.cs

Jamieson Rhyne
  • 428
  • 3
  • 10
  • I'm slightly unsure about the provided example. The way I read it is from ConfigureService, call Services.AddOptions() without parameters, and the builder.Register calls inside of the module. Where I'm lost, is where _configuration is declared in relation to the Module. Could you update your example to a full minimal example, reflecting the format of the example methods from the original post? – bigfoot Nov 13 '20 at 19:25
  • 1
    Utilize the RegisterModule method overload that accepts a constructed module with parameters. Ie: containerBuilder.RegisterModule(new MyModule(Configuration)); – Jamieson Rhyne Nov 15 '20 at 03:29
  • 1
    Configuration would be instantiated from within the asp.net core startup, IConfiguration can be injected into Startup class after configured in program.cs, examples found here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#access-configuration-in-startup – Jamieson Rhyne Nov 15 '20 at 03:38
0

Startup.cs

  public void ConfigureContainer(ContainerBuilder builder)
        {
            // Register your own things directly with Autofac here. Don't
            // call builder.Populate(), that happens in AutofacServiceProviderFactory
            // for you.
            builder.RegisterModule(new AutofacModule(Configuration));
        }

AutofacModule.cs

 public class AutofacModule: Module
    {
        private IConfiguration configuration;

        public AutofacModule(IConfiguration configuration)
        {
            this.configuration = configuration;
        }

        protected override void Load(ContainerBuilder builder)
        {
            builder.Register(p => configuration.GetSection("AppAPIKey").Get<ConfigSettings>()).SingleInstance();
            builder.RegisterType<TestService>()
                .As<ITestService>()
                .SingleInstance();
        }
    }