.NetCore IOptions 使用构造函数配置对象

.NetCore IOptions configure object with constructor

在 .NetCore 应用程序中,我想向服务中注入一个在构造函数中具有一些自定义逻辑的设置。特别是,我有一个类似于此的设置部分:

"Email": {
    "FromEmailName" : "My name",
    "FromEmail" : "my_name@mydomain.com"
}

现在,我想将它绑定到一个 class 看起来像:

public class EmailSettings {
    public System.Net.Mail.MailAddress MailAddress { get; } 
 
    public EmailSetting(string fromEmailName, string fromEmail) {
        MailAddress = new MailAddress(fromEmail, fromEmailName);
    }
}

docs 我看到 EmailSettings class 必须有一个无参数的构造函数,但是有什么方法可以通过引入 [=22= 来解决这个问题] class UnparsedEmailSetting,然后执行类似下面的代码?

public static void BindEmailConfig(this IServiceCollection services, IConfiguration configuration) {
    var section = configuration.GetSection("Email");
    services.Configure<UnparsedEmailSettings>(section);
    var emailConfig = section.Get<UnparsedEmailSettings>();
    var emailSettings = new 
    EmailSettings(emailConfig.FromEmailName, emailConfig.FromEmail);
    services.Configure<EmailSettings>(emailSetting); // does not work
    services.RemoveConfiguration<UnparsedEmailSettings); // method does not exist
}

由于添加一个选项基本上意味着添加一个 IOptions<TOptions> 类型的单例,您可以这样做。 Options 没有构造函数,但您可以使用 the create method from the Options extensions.

services.AddSingleton<IOptions<EmailSetting>>(serviceProvider =>
{
    return Options.Create(new EmailSetting(Configuration.GetSection("Email").GetSection("FromEmailName").Value,
                    Configuration.GetSection("Email").GetSection("FromEmail").Value));
});

然后可以像往常一样注入 IOptions<EmailSetting>

第一, from Jakob will work fine, but it won't give you the services that IOptions can provide (such as reloading). If that is important and the information in your question is complete you should be able to do a work-around. Otherwise I strongly suggest .

保留设置:

  "Email": {
    "FromEmailName": "My name",
    "FromEmail": "my_name@mydomain.com"
  }

像这样注册您的配置:

services.Configure<EmailSettings>(Configuration.GetSection("Email"));

并更改您的模型:

public class EmailSettings
{
    public string FromEmailName { get; init; }
    public string FromEmail { get; init; }

    public System.Net.Mail.MailAddress MailAddress => new MailAddress(FromEmail, FromEmailName);
}

这样您的构造函数中就不需要任何特殊逻辑了。在 C#10 中,您也应该能够将 required 添加到 属性。

public required string FromEmail { get; init; }

缺点是您的新 EmailSettings 会暴露更多不必要的成员。但它仍然是不可变的。

如果不公开任何其他成员也很重要,您可以简单地添加一个接口:

public interface IEmailSettings
{
    System.Net.Mail.MailAddress MailAddress { get; }
}

然后像这样更改您的注册:

services.Configure<EmailSettings>(Configuration.GetSection("Email"));
services.AddSingleton<IOptions<IEmailSettings>>(sp =>
    sp.GetRequiredService<IOptions<EmailSettings>>());

现在您可以注射 IOptions<IEmailSettings>。或者如果你想启用重新加载,你可以将注册更改为IOptionsMonitor。:

services.AddSingleton<IOptionsMonitor<IEmailSettings>>(sp =>
{
    return sp.GetRequiredService<IOptionsMonitor<EmailSettings>>();
});