依赖注入 IOption 类库

4
我是一个有用的助手,可以将文本翻译成中文。

我有一个类库,想要从appsettings.json文件中访问Connectionstring。

appsettings.json:

"DatabaseSettings": {
    "ConnectionString": "Server=.;Database=Test;Trusted_Connection=True;"
  },

在 startup.cs 文件中,我有以下代码:
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<DatabaseSettings>(Configuration.GetSection("DatabaseSettings"));
    services.AddOptions();

    services.AddTransient<IConnectionOption, Class1>();
}

在课堂图书馆里。

IConnectionOption.cs

public interface IConnectionOption
{
    void ReadValue();
}

Class1.cs

public class Class1 : IConnectionOption
{
    private readonly DatabaseSettings test;

    public Class1(IOptions<DatabaseSettings> dbOptions)
    {
        test = dbOptions.Value;    
    }    

    public void ReadValue()
    {
        var r = test;
    }
}

现在从 index.cshtml 我想调用类 Library Class1。
public class IndexModel : PageModel
{
    public void OnGet()
    {
        Class1 test = new Class1();
        test.ReadValue();
    }
}

当然,这样是行不通的,因为没有接受零参数的构造函数,我不认为我应该将IOptions作为参数添加进去。但是,我该如何调用类库来读取连接字符串呢?(当我让它工作后,我当然会读取数据并返回而不是连接字符串)。我已经查看了几个示例,包括net core 2.1依赖注入,但我不明白如何直接使用类库,有必要使用控制器吗?!
2个回答

7
如果DatabaseSettings对类库是可访问的,那么Class1类库就没有必要与更多与框架相关的IOptions紧密耦合。理想情况下,Class1可以通过显式构造函数注入来明确依赖于DatabaseSettings
public class Class1 : IConnectionOption {
    private readonly DatabaseSettings test;

    public Class1(DatabaseSettings settings) {
        test = settings;    
    }    

    public void ReadValue() {
        var r = test;
        //...
    }
}

然后在 Startup 中,可以从配置中提取依赖项并将其注册到 DI 容器中。

public void ConfigureServices(IServiceCollection services) {
    var settings = Configuration.GetSection("DatabaseSettings").Get<DatabaseSettings>();
    services.AddSingleton<DatabaseSettings>(settings);    
    services.AddTransient<IConnectionOption, Class1>();
}

那么,每当解析 Class1 时,容器都会知道如何注入 DatabaseSettings 依赖项。另外一个选项也可以是使用工厂委托。
public void ConfigureServices(IServiceCollection services) {
    var settings = Configuration.GetSection("DatabaseSettings").Get<DatabaseSettings>();
    services.AddTransient<IConnectionOption, Class1>(_ => new Class1(settings));
}

这样,当 IndexModel 依赖于 IConnectionOption 进行注入时。

public class IndexModel : PageModel {
    private readonly IConnectionOption connectionOption;

    public IndexModel(IConnectionOption iConnectionOption) {
        connectionOption = iConnectionOption;
    }

    public void OnGet() {
        connectionOption.ReadValue();
        //...
    }
}

当页面模型初始化时,将注入正确的依赖项。


1
赞成不依赖于 IOptions<T>。但是为什么要将 DatabaseSettings 注册为瞬态而不是单例?它永远不会改变。 - Steven
1
@Steven 已修复。实际上应该是单例模式。感谢您指出这一点。 - Nkosi
以下是关于编程的翻译内容:一种更加整洁的注册设置类的方法,避免了丑陋的GetSection(<string>),并在执行期间更改设置时具有热重新加载设置的额外好处:services.AddTransient(provider => provider.GetRequiredService<IOptionsMonitor<DatabaseSettings>>().CurrentValue); - Mathias R
我希望我可以多次点赞!非常棒的答案! - Avrohom Yisroel

2
您正在使用依赖注入,但只是部分地使用了它。您缺少的是:
  1. Register the service in the container (I'm going to assume a better name for Class1):

    services.AddScoped<IConnectionOption, DatabaseConnectionOption>();
    
  2. Make the page receive the service:

    public class IndexModel : PageModel
    {
        private readonly IConnectionOption _IConnectionOption;
    
        public IndexModel(IConnectionOption iConnectionOption)
        {
            _IConnectionOption = iConnectionOption;
        }
    
        public void OnGet()
        {
            _IConnectionOption.ReadValue();
        }
    }
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接