Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using IConfigureOptions to configure an injected dependency

I have created an ASP.NET Core 1.0.1 WebApi project and am trying to initialize an injected dependency with some custom options before using it in my controllers. After searching online I found a few articles (here, here and here explaining how to use IConfigureServices to do just this. Seems pretty simple! Unfortunately, I can't get it to work and I can't figure out why, I'm sure it must be a simple oversight..

I have created a simple project, and added the following classes to illustrate the most basic scenario:

public class Tester
{
    public void Initialize(TestOptions options)
    {
        //do something with options.
    }
}

public class TestConfigurator : IConfigureOptions<TestOptions>
{
    private Tester _tester;

    public TestConfigurator(Tester tester)
    {
        _tester = tester;
    }

    public void Configure(TestOptions options)
    {
        _tester.Initialize(options);
    }
}

public class TestOptions
{
}

The 'Tester' class gets injected into the constructor of a Controller class:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    public ValuesController(Tester tester)
    {
        //do something with tester..
    }
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}

Finally, I have added the following configuration in ConfigureServices of the Startup class:

    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddApplicationInsightsTelemetry(Configuration);
        services.AddOptions();
        services.AddMvc();
        services.AddSingleton<Tester, Tester>();
        services.AddSingleton<IConfigureOptions<TestOptions>, TestConfigurator>();
    }

When I run the project and call the 'api/values' call, the Tester class is created and injected into the ValuesController, but the TestConfigurator class never gets constructed and so the class never gets Initialized with the options class. What am I missing?

UPDATE The answers below are of course all valid to this simplified example. I realize now that I oversimplified a bit, as the dependency I'm using (illustrated here as Tester) is from a 3rd party library, so I don't have the constructor to play with. Wrapping the 3rd party class in an extended class will do the trick, but if anybody has an alternative suggestion on manipulating it without modifying its constructor, then I'm still open to suggestions, thanks.

like image 528
Chris Avatar asked Oct 11 '25 18:10

Chris


2 Answers

Ok, now I got it, I feel silly for all the edits.

you are using IOptions wrong, and it got me all confused.

implementing a custom IConfigurationOptions<> gives you the abilty to either configure your options from database, or to just use a different class (instead of a lambda)

what you are trying to do, is instantiate a Tester class based on those options, this is fine - but it's not the IConfigureOptions<> job.

in order to initialize your Tester class based on the TestOptions you should create a constructor on the Tester class that receives it like this

public class Tester
{
    public Tester(IOptions<TestOptions> options)
    {
        //do something with options.
    }
}

and what you are trying to do will work.

like image 142
gilmishal Avatar answered Oct 14 '25 11:10

gilmishal


Taken from Asp.Net Core Configuration Documentation and adapted to your example

Assuming

public class TestOptions {
    public string SomeOption { get; set; }
}

Options can be injected into your application using the IOptions<TOptions> accessor service.

You could try abstracting Tester and registering that with the service collection.

public interface ITester {
    //tester contract    
}

public class Tester : ITester {
    public Tester(IOptions<TestOptions> options) {
        //do something with test options.
    }
}

To setup the IOptions<TOptions> service you call the AddOptions extension method during startup in your ConfigureServices method. You configure options using the Configure<TOptions> extension method. You can configure options using a delegate or by binding your options to configuration:

public void ConfigureServices(IServiceCollection services) {

    services.AddApplicationInsightsTelemetry(Configuration);

    // Setup options with DI
    services.AddOptions();

    // Configure TestOptions using config by installing Microsoft.Extensions.Options.ConfigurationExtensions
    services.Configure<TestOptions>(Configuration);

    // Configure TestOptions using code
    services.Configure<TestOptions>(testOptions => {
        testOptions.SomeOption = "value1_from_action";
    });

    // Add framework services.
    services.AddMvc();

    // Add your services.
    services.AddSingleton<ITester, Tester>();
}

And finally just refactor the controller to depend on the abstraction instead of the concretion.

[Route("api/[controller]")]
public class ValuesController : Controller {
    public ValuesController(ITester tester) {
        //do something with tester..
    }
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get() {
        return new string[] { "value1", "value2" };
    }
}
like image 39
Nkosi Avatar answered Oct 14 '25 09:10

Nkosi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!