如何从类中访问 ASP.NET Core DI 容器

mic*_*ael 4 c# dependency-injection asp.net-core

我正在使用 Asp.net core 学习 IoC 和 DI。我已经将我的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"));
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我正在注入 AppSettings 类。我像这样访问这个类没有问题:

private readonly AppSettings _appSettings;

public HomeController(UserManager<ApplicationUser> userManager, 
    ApplicationDbContext dbContext,
    ViewRender view,
    IHostingEnvironment env,
    IOptions<AppSettings> appSettings
    )
{
}
Run Code Online (Sandbox Code Playgroud)

将其传递到控制器的构造函数中效果很好。

但我需要访问类中的 AppSettings,并希望有一个静态方法可以用来将该类注入到任何随机类中。这可能吗?或者我是否需要将其注入控制器并将其传递给每个其他类?

Ste*_*ven 5

防止将IOptions<T>依赖项注入应用程序类。这样做充满了此处描述的问题。

同样,将 a 注入AppSettings到类中也是一个问题,因为这意味着所有类都会注入所有配置值,而它们只使用其中的一两个值。这使得这些类更难测试,并且更难确定此类实际需要哪个配置值。它还将配置的验证推送到应用程序内部,这使您的应用程序更加脆弱;当配置值丢失时,您会在很晚之后发现,而不是在应用程序启动时发现。

类应该在其构造函数中指定所需的内容,并且不应通过其他类传递这些依赖项。这对于注入的组件和配置值都适用。这意味着,如果一个类需要特定的配置值,它应该在其构造函数中指定该值并且仅指定该值。

更新

包含您提到的这个方法的“电子邮件类”SendVerification()对我来说似乎是一个应用程序组件。由于该类发送实际的邮件,因此它需要所有这些邮件配置设置;不是控制器!因此,这些设置应该直接注入到该组件中。但再次强调,不要向该类中注入任何通用内容(例如IOptions<T>AppSettings或)。IConfiguration1 尽可能具体地描述该类的需求,2. 确保在应用程序启动时读取配置值,这样可以让应用程序在启动时快速失败。

所以我想象你的“邮件类”由抽象定义如下:

public interface IVerificationSender
{
    void SendVerification(User user);
}
Run Code Online (Sandbox Code Playgroud)

这允许您的控制器依赖于这个抽象。请注意,任何组件都不应创建应用程序组件本身的依赖关系。这是一种称为“控制狂”的反模式(请参阅本书)。

// 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);  
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们有了一个IVerificationSender使用邮件发送消息的实现(这就是您的“邮件类”)。该类伴随着一个参数对象,该对象保存该类所需的所有配置值(但绝对仅此而已)。

// 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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这种方法,控制器和 的注册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));
Run Code Online (Sandbox Code Playgroud)

可能settings.json如下所示:

public interface IVerificationSender
{
    void SendVerification(User user);
}
Run Code Online (Sandbox Code Playgroud)