我正在学习IoC& DI与Asp.net核心。 我已经设置了我的dbcontext和其他类注入我的控制器。 目前我的startup.cs看起来像这样:
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 5;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
}).AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
正如您可以看到的那样,我正在注入AppSettings类。我这样访问这个类没有问题:
private readonly AppSettings _appSettings;
public HomeController(UserManager<ApplicationUser> userManager,
ApplicationDbContext dbContext,
ViewRender view,
IHostingEnvironment env,
IOptions<AppSettings> appSettings
)
{
将其传递给控制器的构造函数可以正常工作。
但我需要在类中访问AppSettings,并希望有一个静态方法可以用来将类注入任何随机类。这可能吗?或者我是否需要将其注入控制器并将其传递给其他类?
答案 0 :(得分:2)
防止将IOptions<T>
依赖项注入应用程序类。这样做有很多问题,如here所述。
同样是将AppSettings
注入类中是一个问题,因为这意味着所有类都会注入所有配置值,而它们只使用其中一个或两个值。这使得这些类更难以测试,并且要弄清楚这个类实际需要哪个配置值变得更加困难。它还将配置验证推送到应用程序内部,这使您的应用程序更加脆弱;稍后您会发现缺少配置值,而不是找出应用程序何时启动。
类应该在其构造函数中指定它需要的东西,并且不应该通过其他类传递这些依赖项。这适用于注入的组件和配置值。这意味着如果一个类需要特定的配置值,它应该在其构造函数中指定 - 并且只指定该值。
<强>更新强>
您提及的包含此SendVerification()
方法的“电子邮件类”对我来说似乎是一个应用程序组件。由于该类发送实际邮件,因此需要所有这些邮件配置设置;不是控制器!因此,应将这些设置直接注入该组件。但同样,请不要将任何一般内容(例如IOptions<T>
,AppSettings
或IConfiguration
)注入该类。 1尽可能具体地说明该类需要的内容2.确保在应用程序启动时读取配置值,以便在应用程序启动时让应用程序快速失败。
所以我想你的“邮件类”由抽象定义如下:
public interface IVerificationSender
{
void SendVerification(User user);
}
这允许您的控制器依赖于此抽象。请注意,任何组件都不应创建应用程序组件本身的依赖项。这是一种称为 Control Freak 的反模式(参见this book)。
// Controller that depends on the IVerificationSender abstraction
public class HomeController : Controller
{
private readonly IVerificationSender verificationSender;
public HomeController(IVerificationSender verificationSender, ...) {
this.verificationSender = verificationSender;
}
public void SomeAction() {
this.verificationSender.SendVerification(user);
}
}
现在我们有一个IVerificationSender
实现,它使用邮件发送邮件(这是你的“邮件类”)。该类由Parameter Object伴随,该here包含此类所需的所有配置值(但绝对没有更多)。
// Settings class for the IVerificationSender implementation
public class SmtpVerificationSenderSettings
{
public string MailHost { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool EnableSsl { get; set; }
// etc
}
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>();
// Verify the settings object
if (string.IsNullOrWhiteSpace(settings.MailHost)
throw new ConfigurationErrorsException("MailSettings MailHost missing.");
if (string.IsNullOrWhiteSpace(settings.MailHost)
throw new ConfigurationErrorsException("MailSettings UserName missing.");
// etc
// Register the EmailVerificationSender class
services.AddSingleton<IVerificationSender>(new EmailVerificationSender(settings));
settings.json
可能如下所示:
{
"SmtpVerificationSenderSettings": {
"MailHost" : "localhost",
"UserName" : "foobar",
// etc
}
}