简体   繁体   English

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

[英]How to access the ASP.NET Core DI Container from within a class

I am learning IoC & DI with Asp.net core.我正在使用 Asp.net 核心学习 IoC 和 DI。 I have setup my DbContext and other classes to be injected into my controllers.我已经设置了要注入到控制器中的DbContext和其他类。

Currently my startup.cs looks like this:目前我的 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"));

As you can see amongst other things I am injecting AppSettings class.正如您所看到的,我正在注入 AppSettings 类。 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.将它传递给控制器​​的构造函数可以正常工作。

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.但是我需要在一个类中访问 AppSettings,并且希望有一个静态方法可以用来将该类注入到任何随机类中。 Is this possible?这可能吗? Or do I need to inject it into the controller and pass it to each other class?还是我需要将其注入控制器并将其传递给其他类?

Prevent injecting IOptions<T> dependencies into application classes.防止将IOptions<T>依赖项注入应用程序类。 Doing so is riddled with problems as described here .如此处所描述的那样,这样做充满了问题

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.同样,将AppSettings注入类也是一个问题,因为这意味着所有类都获得了注入的所有配置值,而它们只使用其中的一个或两个值。 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.这意味着如果一个类需要特定的配置值,它应该在其构造函数中指定该值,并且仅指定该值。

Update更新

The "email class" that contains this SendVerification() method that you mention, seems like an application component to me.包含您提到的此SendVerification()方法的“电子邮件类”对我来说似乎是一个应用程序组件。 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.但同样,不要向该类注入任何通用内容(例如IOptions<T>AppSettingsIConfiguration )。 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. 1 尽可能具体地满足该类的需求; 2. 确保在应用程序启动时读取配置值,以便在应用程序启动时让应用程序快速失败。

So I imagine your "mail class" to be defined by an abstraction as follows:所以我想你的“邮件类”由一个抽象定义如下:

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

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 ).这是一种被称为Control Freak的反模式(参见本书)。

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

Now we have a IVerificationSender implementation that uses mail to send messages (that's your "mail class" thingy).现在我们有一个使用邮件发送消息的IVerificationSender实现(这是你的“邮件类”东西)。 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);
            }
        }
    }
}

Using this approach, registration of both the controller and the EmailVerificationSender should be trivial.使用这种方法,控制器和EmailVerificationSender的注册应该很简单。 You can even use this SmtpVerificationSenderSettings as serializable object that is loaded from the configuration file:您甚至可以将此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));

Where the settings.json might look as follows: settings.json可能如下所示:

{
    "SmtpVerificationSenderSettings": {
        "MailHost" : "localhost",
        "UserName" : "foobar",
        // etc
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从 Asp.Net Core 的 ConfigureServices 方法中访问添加到 DI 容器的服务 - How to access a service added to DI container from inside Asp.Net Core's ConfigureServices method ASP.Net Core从DI容器获取服务 - ASP.Net Core Get Services From DI Container 从 Static 工厂访问 ASP.NET 核心 DI 容器 Class - Accessing ASP.NET Core DI Container From Static Factory Class 如何验证 ASP.NET Core 中的 DI 容器? - How do I validate the DI container in ASP.NET Core? 如何将方法注入 ASP.NET 内核中的 DI 容器? - How to inject method into DI Container in ASP.NET Core? 在 ConfigureServices 中使用 ASP.NET Core DI 解析实例 - Resolving instances with ASP.NET Core DI from within ConfigureServices 如何在 ASP.NET Core 集成测试中覆盖来自其他容器的 DI 注册 - How to override DI registration from other container in ASP.NET Core integration test ASP.NET Core DI在类库中? - ASP.NET Core DI in a class library? 如何使用默认的ASP.NET Core DI容器在类中注入单个接口的多个服务类实现 - How to inject multiple service class implementation of single interface in class using default asp.net core DI container 在 ASP.net 核心应用程序中使用 IMemoryCache 和 Unity DI Container - Use IMemoryCache with Unity DI Container in ASP.net core application
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM