如何从类访问Asp.net Core DI容器 [英] How to access Asp.net Core DI Container from Class

查看:82
本文介绍了如何从类访问Asp.net Core DI容器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习IoC&带Asp.net核心的DI。
我已经设置了dbcontext和其他类以注入到控制器中。
当前,我的startup.cs如下所示:

I am learning IoC & DI with Asp.net core. I have setup my dbcontext and other classes to be injected into my controllers. Currently my startup.cs looks like this:

        // 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类。我可以像这样访问此类:

As you can see amongst other things I am injecting AppSettings class. I have no problem accessing this class like this:

    private readonly AppSettings _appSettings;


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

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

Passing it into the constructor of a controller works fine.

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

But I need to access the AppSettings in a class, and was hoping there was a static method I could use to inject the class into any random class. Is this possible? Or do I need to inject it into the controller and pass it to each other class?

推荐答案

防止注入 IOptions< T> 依赖关系到应用程序类中。 此处

Prevent injecting IOptions<T> dependencies into application classes. Doing so is riddled with problems as described here.

类似地,将 AppSettings 注入到类中是一个问题,因为这意味着所有类都将注入所有配置值,而它们仅使用其中一个或两个值。这使这些类很难测试,并且要弄清楚此类实际需要的配置值也变得更加困难。它还将配置的验证推送到您的应用程序内部,这使您的应用程序更加脆弱。

Likewise is the injection of a AppSettings into classes a problem, because this means that all classes get all configuration values injected, while they only use one or two of those values. This makes those classes harder to test, and it becomes much harder to figure out which configuration value such class actually requires. It also pushes the verification of the configuration to inside your application, which makes your application much more fragile; you'll find out much later when a configuration value is missing, instead of finding out when the application is started.

一个类应在其构造函数中指定它需要的内容,否则,您将在以后发现缺少配置值时发现更多信息。 ,并且不应将这些依赖项传递给其他类。这对于注入的组件和配置值均成立。这意味着,如果类需要特定的配置值,则应在其构造函数中指定该值,并且仅指定该值。

A class should specify the things it requires in its constructor, and should not pass those dependencies around through other classes. This holds for both injected components and configuration values. This means that in case a class requires a specific configuration value, it should specify that -and only that- value in its constructor.

更新

包含您提到的 SendVerification()方法的电子邮件类似乎是一个应用程序组件我。由于该类发送实际的邮件,因此它是需要所有这些邮件配置设置的类。不是控制器!因此,这些设置应直接注入该组件中。但是同样,请避免注入任何常规内容(例如 IOptions< T> AppSettings IConfiguration )放入该类。 1尽可能具体地说明该类的需求,并确保2在应用程序启动时读取配置值,以便在应用程序启动时让应用程序快速失败。

The "email class" that contains this SendVerification() method that you mention, seems like an application component to me. Since that class sends the actual mail, it is the one that requires all those mail configuration settings; not the controller! So those settings should be injected directly into that component. But again, refrain from injecting anything general (such as IOptions<T>, AppSettings or IConfiguration) into that class. 1 Be as specific as possible as what that class needs and 2. make sure configuration values are read at application startup where you can let the application fail fast when the application starts up.

因此,我想您的邮件类将通过以下抽象定义:

So I imagine your "mail class" to be defined by an abstraction as follows:

public interface IVerificationSender
{
    void SendVerification(User user);
}

这使您的控制器可以依赖此抽象。请注意,任何组件都不应创建应用程序组件本身的依赖关系。这是一种称为 Control Freak 的反模式(请参见这本书)。 / p>

This allows your controller to take a dependency on this abstraction. Note that no component should create dependencies of application components itself. This is an anti-pattern known as Control Freak (see 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 使用邮件发送邮件的实现(这就是您的邮件类别)。该类与参数对象伴随,该对象包含该类所需的所有配置值(但

Now we have a IVerificationSender implementation that uses mail to send messages (that's your "mail class" thingy). That class is companioned by a Parameter Object that holds all the configuration values that this class requires (but absolutely nothing more than that).

// 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 用作从配置文件加载的可序列化对象:

Using this approach, registration of both the controller and the EmailVerificationSender should be trivial. You can even use this SmtpVerificationSenderSettings as serializable object that is loaded from the configuration file:

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

这篇关于如何从类访问Asp.net Core DI容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆