避免将IOptions<T>
依赖注入到应用程序类中。这样做存在很多问题,如此处所述。
同样地,将AppSettings
注入到类中也是一个问题,因为这意味着所有的类都会被注入所有的配置值,而它们只使用其中的一两个。这使得这些类难以测试,并且变得更加难以确定这个类实际上需要哪个配置值。它还将验证配置推向了应用程序内部,这使得应用程序非常脆弱;你会在应用程序启动时发现缺少了一个配置值,而不是在之前找出来。
一个类应该在其构造函数中指定它需要的东西,并且不应该通过其他类传递这些依赖项。这适用于注入组件和配置值。这意味着如果一个类需要特定的配置值,它应该在构造函数中指定那个-仅仅那个-值。
更新
你提到的包含此SendVerification()
方法的“邮件类”对我来说似乎是一个应用程序组件。由于该类发送实际的邮件,因此它是需要所有这些邮件配置设置的类;而不是控制器!因此,应该将这些设置直接注入到该组件中。但是,仍要避免将任何通用内容(如IOptions<T>
、AppSettings
或IConfiguration
)注入到该类中。1.尽可能明确该类需要的内容,2.确保在应用程序启动时读取配置值,可以让应用程序在启动时快速失败。
因此,我想象您的“邮件类”由以下抽象定义:
public interface IVerificationSender
{
void SendVerification(User user);
}
这使得您的控制器可以依赖于此抽象。请注意,任何组件都不应该自行创建应用程序组件的依赖关系。 这是一个反模式,称为Control Freak(请参见此书)。
public class HomeController : Controller
{
private readonly IVerificationSender verificationSender;
public HomeController(IVerificationSender verificationSender, ...) {
this.verificationSender = verificationSender;
}
public void SomeAction() {
this.verificationSender.SendVerification(user);
}
}
现在我们有一个使用邮件发送消息的
IVerificationSender
实现(也就是您所谓的“mail class”)。该类配合着一个
参数对象,该对象仅保存该类所需的所有配置值(没有多余的内容)。
public class SmtpVerificationSenderSettings
{
public string MailHost { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool EnableSsl { get; set; }
}
public class EmailVerificationSender : IVerificationSender
{
private readonly SmtpVerificationSenderSettings settings;
public EmailVerificationSender(SmtpVerificationSenderSettings settings) {
if (settings == null) throw new ArgumentNullException("settings");
this.settings = settings;
}
public void SendVerification(User user) {
using (var client = new SmtpClient(this.settings.MailHost, 25)) {
smtpClient.EnableSsl = this.settings.EnableSsl;
using (MailMessage mail = new MailMessage()) {
mail.From = new MailAddress("info@foo", "MyWeb Site");
mail.To.Add(new MailAddress(user.Email));
mail.Body = $"Hi {user.Name}, Welcome to our site.";
client.Send(mail);
}
}
}
}
使用这种方法,控制器和
EmailVerificationSender
的注册都应该是轻松的。您甚至可以将这个
SmtpVerificationSenderSettings
作为可序列化对象,从配置文件中加载:
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("settubgs.json");
.Build();
var settings = config.GetSection("SmtpVerificationSenderSettings")
.Get<SmtpVerificationSenderSettings>();
if (string.IsNullOrWhiteSpace(settings.MailHost)
throw new ConfigurationErrorsException("MailSettings MailHost missing.");
if (string.IsNullOrWhiteSpace(settings.MailHost)
throw new ConfigurationErrorsException("MailSettings UserName missing.");
services.AddSingleton<IVerificationSender>(new EmailVerificationSender(settings));
settings.json
文件可能如下所示:
{
"SmtpVerificationSenderSettings": {
"MailHost" : "localhost",
"UserName" : "foobar",
}
}