Получение значения конфигурации в ASP.NET 5 (vNext)
Я борюсь с некоторыми понятиями в ASP.NET 5 (vNext).
Один из них - это подход Подход к зависимостям, используемый для конфигурации. Похоже, что я должен пройти параметр через стек. Возможно, я что-то недопонимаю или делаю неправильно.
Представьте, что у меня есть свойство config с именем "contactEmailAddress". Я буду использовать это свойство config для отправки электронной почты при размещении нового заказа. Учитывая этот сценарий, мой стек ASP.NET 5 будет выглядеть следующим образом:
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
public class AppSettings
{
public string ContactEmailAddress { get; set; }
}
config.json
{
"AppSettings": {
"ContactEmailAddress":"[email protected]"
}
}
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
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>();
}
}
Как показывает вышеприведенный пример, я пропускаю AppSettings
через весь стек. Это не кажется правильным. Чтобы я больше не беспокоился, этот подход не будет работать, если я попытаюсь использовать стороннюю библиотеку, которая должна получить доступ к настройкам конфигурации. Как сторонняя библиотека может получить доступ к настройкам конфигурации? Я что-то не понимаю? Есть ли лучший способ сделать это?
Ответы
Ответ 1
Вы запутываете 2 разных поставщика ресурсов времени выполнения, AppSettings и Инъекции зависимостей.
AppSettings, обеспечивает доступ во время выполнения к конкретным значениям приложения, таким как строки UICulture, контактный адрес электронной почты и т.д.
Контейнеры DI - это фабрики, которые управляют доступом к службам и их объектам жизненного цикла. Например, если MVC-контроллеру необходим доступ к вашему 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>();
}
Затем, если нашему домашнему контроллеру нужен доступ к вашему EmailService
, мы добавим зависимость от него Интерфейс, добавив его как параметр в конструктор контроллера
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);
Цель Injection Dependancy - управлять доступом и сроками службы.
В предыдущем примере в нашем приложении Startup
мы настроили DI Factory, чтобы связать запросы приложений для IEmailService
с EmailService
. Поэтому, когда наши Контроллеры создаются с помощью MVC Framework, структура отмечает, что наш Home Controller
ожидает IEmailService
, фреймворк проверяет нашу коллекцию приложений. Он находит инструкции отображения и Inject a Singleton
EmailService
(потомок оккупирующего интерфейса) в наш домашний контроллер.
Суперполиморфный фактор - алодозный!
Почему это важно?
Если ваше контактное письмо изменилось, вы измените значение AppSetting
и закончите. Все запросы для "ContactEmail" из ConfigurationManager
глобально изменены. Строки просты. Нет необходимости в инъекции, когда мы можем просто хешировать.
Если ваш репозиторий, служба электронной почты, служба ведения журналов и т.д. изменяется, вам нужен глобальный способ изменить все ссылки на эту службу. Ссылка на службу не так легко передается как непреложные строковые литералы. Консоль службы должна обрабатываться Factory для настройки параметров службы и зависимостей.
Итак, через год вы разработаете RobustMailService
:
Class RobustMailService : IEmailService
{
....
}
Пока ваш новый 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>();
}
Ответ 2
Это может быть достигнуто с помощью службы оценки IOptions, поскольку, кажется, вы пытались.
Мы можем начать с создания класса со всеми переменными, которые требуется вашему контроллеру из конфигурации.
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; }
}
Теперь нам нужно сообщить промежуточному программному обеспечению, что контроллер нуждается в этом классе в конструкторе контроллера, используя инъекцию зависимостей, мы делаем это с помощью службы доступа 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);
}
}
Чтобы получить переменные из ваших файлов конфигурации, мы создаем конструктор для класса запуска и свойство конфигурации.
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();
}
Теперь нам нужно убедиться, что трубопровод действительно снабжает контроллер этой услугой.
В вашем методе ConfigureServices в вашем классе Startup вы хотите использовать промежуточное программное обеспечение 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...
}
Для получения дополнительной информации см. главу "Использование опций и объектов конфигурации" в Документы ASPCore