如何从 Class 访问 Asp.net 核心 DI 容器

How to access Asp.net Core DI Container from Class

我正在学习 Asp.net 核心的 IoC 和 DI。 我已经设置了我的 dbcontext 和其他 classes 以注入我的控制器。 目前我的 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 class。我可以像这样访问 class 没有问题:

    private readonly AppSettings _appSettings;


    public HomeController(UserManager<ApplicationUser> userManager, 
        ApplicationDbContext dbContext,
        ViewRender view,
        IHostingEnvironment env,
        IOptions<AppSettings> appSettings
        )
    {

将其传递到控制器的构造函数中工作正常。

但我需要访问 class 中的 AppSettings,并希望有一个静态方法可以用来将 class 注入到任何随机 class 中。这可能吗?还是需要注入到controller中相互传递class?

防止将 IOptions<T> 依赖项注入应用程序 classes。这样做充满了问题here

同样,将 AppSettings 注入 classes 也是一个问题,因为这意味着所有 classes 都会注入所有配置值,而它们只使用一个或两个这些值。这使得那些 classes 更难测试,并且更难弄清楚 class 实际需要哪个配置值。它还将配置验证推送到您的应用程序内部,这使您的应用程序更加脆弱;当缺少配置值时,您会在很久以后发现,而不是在应用程序启动时发现。

A class 应该在其构造函数中指定它需要的东西,并且不应该通过其他 classes 传递这些依赖关系。这适用于注入的组件和配置值。这意味着如果 class 需要特定的配置值,它应该在其构造函数中指定该值,并且仅指定该值。

更新

包含您提到的 SendVerification() 方法的 "email class" 对我来说似乎是一个应用程序组件。由于 class 发送实际邮件,因此需要所有这些邮件配置设置;不是控制器!所以这些设置应该直接注入到那个组件中。但同样,不要将任何通用内容(例如 IOptions<T>AppSettingsIConfiguration)注入 class。 1 尽可能具体说明 class 需要什么,以及 2. 确保在应用程序启动时读取配置值,这样您可以让应用程序在启动时快速失败。

所以我想象你的 "mail class" 由抽象定义如下:

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 实现,它使用邮件发送消息(那是你的 "mail class" 东西)。 class 伴随着 Parameter Object,它包含此 class 所需的所有配置值(但绝对仅此而已)。

// 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
    }
}