使用IConfigureOptions来配置注入的依赖项

本文关键字:依赖 注入 配置 IConfigureOptions 使用 | 更新日期: 2023-09-27 18:09:11

我已经创建了一个ASP。. NET Core 1.0.1 WebApi项目,我试图在我的控制器中使用它之前,用一些自定义选项初始化注入的依赖项。在网上搜索之后,我找到了一些文章(在这里,在这里和在这里),解释如何使用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
{
}

'Tester'类被注入到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>();
    }

当我运行项目并调用'api/values'调用时,Tester类被创建并注入到ValuesController中,但TestConfigurator类从未被构造,因此类从未被初始化与options类。我错过了什么?

更新下面的答案当然都适用于这个简化的例子。现在我意识到我有点过于简化了,因为我使用的依赖项(此处为Tester)来自第三方库,所以我没有构造函数可以使用。将第三方类包装在一个扩展类中可以达到这个目的,但是如果有人有在不修改构造函数的情况下操作它的替代建议,那么我仍然愿意接受建议,谢谢。

使用IConfigureOptions来配置注入的依赖项

好了,现在我明白了,我觉得所有的编辑都很愚蠢。

你用错了IOptions,这让我很困惑。

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

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

为了初始化基于TestOptionsTester类,你应该在Tester类上创建一个构造函数来接收它,就像这样

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

和你想做的事情将会起作用

摘自Asp。Net Core配置文档,并适应您的示例

假设

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>服务,请调用AddOptionsConfigureServices方法中启动时使用扩展方法。使用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" };
    }
}

teststoptions应该来自哪里?您是否试图从设置文件中自动映射它?我认为你是过度思考这应该如何工作,除非有一些原因,你必须使用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
{
}

然后使用下面两种方法中的一种来配置这些选项(取决于它是来自config还是必须手动构建)。

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