使用IConfigureOptions配置注入的依赖项

时间:2016-09-28 15:47:28

标签: c# asp.net asp.net-core asp.net-core-mvc

我创建了一个ASP.NET Core 1.0.1 WebApi项目,并尝试在我的控制器中使用它之前使用一些自定义选项初始化注入的依赖项。在线搜索后,我发现了一些文章(hereherehere,解释了如何使用IConfigureServices来做到这一点。看起来非常简单!不幸的是,我无法得到它工作,我无法弄清楚为什么,我确信它必须是一个简单的疏忽..

我创建了一个简单的项目,并添加了以下类来说明最基本的场景:

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
{
}

&#39;测试员&#39; class被注入到Controller类的构造函数中:

[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" };
    }
}

最后,我在Startup类的ConfigureServices中添加了以下配置:

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

当我运行项目并调用&#39; api / values&#39;调用时,会创建Tester类并将其注入到ValuesController中,但TestConfigurator类永远不会被构造,因此类永远不会使用options类进行初始化。我错过了什么?

UPDATE 下面的答案当然对这个简化的例子都有效。我现在意识到我过度简化了一点,因为我使用的依赖(在这里作为Tester说明)来自第三方库,所以我没有使用构造函数。在扩展类中包装第三方类将会有所帮助,但如果有人在不修改构造函数的情况下有另外的操作建议,那么我仍然愿意接受建议,谢谢。

3 个答案:

答案 0 :(得分:4)

好的,现在我明白了,我对所有的编辑感到愚蠢。

你正在使用IOptions错误,这让我感到困惑。

实现自定义IConfigurationOptions<>使您可以从数据库配置选项,或者只使用不同的类(而不是lambda)

你要做的是,根据这些选项实例化一个Tester类,这很好 - 但它不是IConfigureOptions<>工作。

为了根据Tester初始化您的TestOptions类,您应该在Tester类上创建一个构造函数,以便像这样接收它

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

你想做的事情会有效。

答案 1 :(得分:2)

TestOptions应该来自哪里?您是否尝试从设置文件中自动映射它?我认为你过分思考这应该如何工作,除非有一些原因你必须使用initialize而不是构造函数注入。

您要做的就是为测试人员提供一些选项,对吗?

如果是这样,请使用基本的IOptions功能 - 无需使用IConfigureOptions

进展
public class Tester
{
    private TestOptions _options;
    public Tester(IOptions<TestOptions> options)
    {
       _options = options.Value;
    }
}
// don't need this anymore
/* 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
{
}

然后使用以下两种方法之一配置选项(取决于它是来自配置还是必须手动构建)。

public void ConfigureServices(IServiceCollection services)
{
        // Add framework services.
        services.AddApplicationInsightsTelemetry(Configuration);
        services.AddOptions();
        services.AddMvc();
        services.AddSingleton<Tester, Tester>();
        // Configure TestOptions using config 
        services.Configure<TestOptions>(Configuration);

        // Configure MyOptions using code
        services.Configure<TestOptions>(testOptions =>
        {
           // initialize them here, e.g. testOptions.Foo = "Bar"
        });
}

答案 2 :(得分:1)

取自Asp.Net Core Configuration Documentation并适应您的示例

假设

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

可以使用选项将选项注入您的应用程序   IOptions<TOptions>访问者服务。

您可以尝试抽象Tester并将其注册到服务集合。

public interface ITester {
    //tester contract    
}

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

要设置IOptions<TOptions>服务,请拨打AddOptions   在ConfigureServices方法启动期间的扩展方法。   您可以使用Configure<TOptions>扩展方法配置选项。   您可以使用委托或绑定选项来配置选项   配置:

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>();
}

最后只是重构控制器以依赖抽象而不是凝固。

[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" };
    }
}