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.
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.
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 theAddOptions
extension method during startup in yourConfigureServices
method. You configure options using theConfigure<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" };
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With