简体   繁体   English

在ASP.NET 5中获取配置值(vNext)

[英]Getting a Configuration Value in ASP.NET 5 (vNext)

I am struggling with some concepts in ASP.NET 5 (vNext) . 我正在努力解决ASP.NET 5(vNext)中的一些概念。

One of those is the Dependency Injection approach used for configuration. 其中之一是用于配置的依赖注入方法。 It seems like I have to pass a parameter all the way through the stack. 好像我必须在堆栈中一直传递一个参数。 I'm probably misunderstanding something or doing it wrong. 我可能误解了某些事情或做错了。

Imagine I have a config property named "contactEmailAddress". 想象一下,我有一个名为“contactEmailAddress”的配置属性。 I'll use that config property to send an email when a new order is placed. 我会使用该配置属性在发出新订单时发送电子邮件。 With that scenario in mind, my ASP.NET 5 stack will look like this: 考虑到这种情况,我的ASP.NET 5堆栈将如下所示:

Startup.cs Startup.cs

public class Startup
{        
  public IConfiguration Configuration { get; set; }
  public Startup(IHostingEnvironment environment)
  {
    var configuration = new Configuration().AddJsonFile("config.json");
    Configuration = configuration;
  }

  public void ConfigureServices(IServiceCollection services)
  {
    services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings"));
    services.AddMvc();
  }

  public void Configure(IApplicationBuilder app)
  {
    app.UseErrorPage();
    app.UseMvc(routes =>
      {
        routes.MapRoute("default",
          "{controller}/{action}/{id}",
          defaults: new { controller = "Home", action = "Index" });
      }
    );
    app.UseWelcomePage();
  }

AppSettings.cs AppSettings.cs

public class AppSettings
{
 public string ContactEmailAddress { get; set; }
}

config.json config.json

{
  "AppSettings": {
    "ContactEmailAddress":"support@mycompany.com"  
  }
}

OrderController.cs OrderController.cs

[Route("orders")]
public class OrdersController : Controller
{    
  private IOptions<AppSettings> AppSettings { get; set; }

  public OrdersController(IOptions<AppSettings> appSettings)
  {
    AppSettings = appSettings;
  }

  [HttpGet("new-order")]
  public IActionResult OrderCreate()
  {
    var viewModel = new OrderViewModel();
    return View(viewModel);
  }

  [HttpPost("new-order")]
  public IActionResult OrderCreate(OrderViewModel viewModel)
  {

    return new HttpStatusCodeResult(200);
  } 
}

Order.cs Order.cs

public class Order()
{
  public void Save(IOptions<AppSettings> appSettings)
  {
    // Send email to address in appSettings
  }

  public static List<Order> FindAll(IOptions<AppSettings> appSettings)
  {
    // Send report email to address in appSettings
    return new List<Order>();
  }
}

As the example above shows, I'm passing AppSettings through the entire stack. 如上例所示,我正在通过整个堆栈传递AppSettings This does not feel correct. 这感觉不正确。 To further my worries, this approach will not work if I'm attempt to use a third-party library that needs to access configuration settings. 为了进一步担心,如果我尝试使用需要访问配置设置的第三方库,这种方法将无效。 How can a third-party library access configuration settings? 第三方库如何访问配置设置? Am I misunderstanding something? 我误会了什么吗? Is there a better way to do this? 有一个更好的方法吗?

You are entangling 2 different run time resource provider, AppSettings and Dependency Injection . 您正在纠缠2个不同的运行时资源提供程序, AppSettings依赖注入

AppSettings , provides run-time access to Application specific values like UICulture strings, Contact Email , etc. AppSettings ,提供对特定于应用程序的值的运行时访问,如UICulture字符串, 联系电子邮件等。

DI Containers are factories that Manage access to Services and their lifetime scopes. DI容器是管理服务访问及其生命周期范围的工厂。 For example, If a MVC Controller needed access to your EmailService , you would configure 例如,如果MVC Controller需要访问您的EmailService ,您将进行配置

   public void ConfigureServices(IServiceCollection services)
   {
      // Add all dependencies needed by Mvc.
      services.AddMvc();

      // Add EmailService to the collection. When an instance is needed,
      // the framework injects this instance to the objects that needs it
      services.AddSingleton<IEmailService, EmailService>();
   }

Then, if our Home Controller needs access to your EmailService , we add a dependency on it's Interface by adding it as a parameter to the Controller constructor 然后,如果我们的Home Controller需要访问您的EmailService ,我们通过将它作为参数添加到Controller构造函数来添加对它的Interface的依赖性

public class HomeController : Controller
{
   private readonly IEmailService _emailService;
   private readonly string _emailContact;

  /// The framework will inject an instance of an IEmailService implementation.
   public HomeController(IEmailService emailService)
   {
      _emailService = emailService;
      _emailContact = System.Configuration.ConfigurationManager.
                   AppSettings.Get("ContactEmail");
   }

   [HttpPost]
   public void EmailSupport([FromBody] string message)
   {
      if (!ModelState.IsValid)
      {
         Context.Response.StatusCode = 400;
      }
      else
      {
         _emailService.Send(_emailContact, message);

The purpose of Dependancy Injection is to manage access and lifetimes of services . Dependancy Injection的目的是管理服务的访问和生命周期

In the previous example, in our Application Startup , we configured the DI Factory to associate application requests for IEmailService with EmailService . 在前面的示例中,在我们的应用程序Startup ,我们将DI Factory配置为将IEmailService应用程序请求与EmailService相关联。 So when our Controllers are instantiate by the MVC Framework , the framework notices that our Home Controller expects IEmailService , the framework checks our Application Services Collection. 因此,当我们的控制器由MVC框架实例化时,框架会注意到我们的Home Controller需要IEmailService ,框架会检查我们的Application Services Collection。 It finds mapping instructions and Inject a Singleton EmailService (a descendant of the occupying Interface) into our Home Controller. 它找到映射指令并将Singleton EmailService (占用接口的后代)注入我们的Home Controller。

Super Polymorphic Factorific - alodocious! Super Polymorphic Factorific - alodocious!

Why is this important? 为什么这很重要?

If your contact email changes, you change the AppSetting value and are done. 如果您的联系电子邮件发生更改,您将更改AppSetting值并完成。 All requests for "ContactEmail" from ConfigurationManager are Globally changed. ConfigurationManager中对“ContactEmail”的所有请求都是全局更改的。 Strings are easy. 字符串很容易。 No need for Injection when we can just hash. 我们可以哈希时不需要注入。

If your Repository, Email Service, Logging Service, etc changes, you want a Global way to change all references to this service. 如果您的存储库,电子邮件服务,日志记录服务等发生更改,您需要一种全局方式来更改对此服务的所有引用。 Service reference aren't as easily transferred as immutable string literals. 服务引用不像不可变字符串文字那样容易转移。 Service instantiation should be handled by a factory to configure the Service's settings and dependencies. 服务实例化应由工厂处理以配置服务的设置和依赖关系。

So, in a year you develop a RobustMailService : 所以,在一年内你开发了一个RobustMailService

Class RobustMailService : IEmailService
{

....

}

As long as your new RobustMailService inherits and implements the IEmailService Interface, you can substitute all references to your mail service Globally by changing : 只要您的新RobustMailService继承并实现了IEmailService接口,您就可以通过更改以下内容替换所有对您的邮件服务的引用:

   public void ConfigureServices(IServiceCollection services)
   {
      // Add all dependencies needed by Mvc.
      services.AddMvc();

      // Add RobustMailService to the collection. When an instance is needed,
      // the framework injects this instance to the objects that needs it 
      services.AddSingleton<IEmailService, RobustMailService>();
   }

This can be achieved using IOptions assessor service as it seems you were trying. 这可以使用IOptions评估服务来实现,因为您似乎正在尝试。

We can begin by creating a class with all of the variables that your controller needs from configuration. 我们可以从创建一个包含控制器所需的所有变量的类开始。

public class VariablesNeeded
{
    public string Foo1{ get; set; }        
    public int Foo2{ get; set; }
}

public class OtherVariablesNeeded
{
    public string Foo1{ get; set; }        
    public int Foo2{ get; set; }
}

We now need to tell the middleware that the controller needs this class in the constructor of the controller using dependency injection, we do this using IOptions accessor service. 我们现在需要告诉中间件控制器需要使用依赖注入在控制器的构造函数中使用此类,我们使用IOptions访问器服务。

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

public class MyController: Controller{    
    private readonly VariablesNeeded _variablesNeeded;

    public MyController(IOptions<VariablesNeeded> variablesNeeded) {
        _variablesNeeded= variablesNeeded.Value;
    }

    public ActionResult TestVariables() {
        return Content(_variablesNeeded.Foo1 + _variablesNeeded.Foo2);
    }
}

To get the variables from your configuration files, we create a constructor for the startup class, and a configuration property. 要从配置文件中获取变量,我们为启动类和配置属性创建构造函数。

public IConfigurationRoot Configuration { get; }

public Startup(IHostingEnvironment env)
{
    /* This is the fairly standard procedure now for configuration builders which will pull from appsettings (potentially with an environmental suffix), and environment variables. */
    var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)    
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
    Configuration = builder.Build();
}

Now we need to make sure the pipeline actually supplies the controller with this service. 现在我们需要确保管道实际上为控制器提供此服务。

In your ConfigureServices method in your Startup class, you want to use the Options middleware, and inject an object of type VariablesNeeded in to the pipeline. 在Startup类的ConfigureServices方法中,您希望使用Options中间件,并将一个VariablesNeeded类型的对象注入到管道中。

public void ConfigureServices(IServiceCollection services)
{
   // Tells the pipeline we want to use IOption Assessor Services
   services.AddOptions();

   // Injects the object VariablesNeeded in to the pipeline with our desired variables
   services.Configure<VariablesNeeded>(x =>
   {
       x.Foo1 = Configuration["KeyInAppSettings"]
       x.Foo2 = Convert.ToInt32(Configuration["KeyParentName:KeyInAppSettings"])
   });

   //You may want another set of options for another controller, or perhaps to pass both to our "MyController" if so, you just add it to the pipeline    
   services.Configure<OtherVariablesNeeded>(x =>
   {
       x.Foo1 = "Other Test String",
       x.Foo2 = 2
   });

   //The rest of your configure services...
}

For more information see the chapter on Using Options and configuration objects in the ASPCore Docs 有关更多信息,请参阅ASPCore文档中有关使用选项和配置对象的章节

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM