user70192 user70192 - 3 months ago 29
ASP.NET (C#) Question

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

I am struggling with some concepts in 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". 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:

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":"support@mycompany.com"
}
}


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


As the example above shows, I'm passing
AppSettings
through the entire stack. 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?

Answer

You are entangling 2 different run time resource provider, AppSettings and Dependency Injection.

AppSettings, provides run-time access to Application specific values like UICulture strings, Contact Email, etc.

DI Containers are factories that Manage access to Services and their lifetime scopes. For example, If a MVC Controller needed access to your EmailService, you would configure

   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

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.

In the previous example, in our Application Startup, we configured the DI Factory to associate application requests for IEmailService with 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. It finds mapping instructions and Inject a Singleton EmailService (a descendant of the occupying Interface) into our Home Controller.

Super Polymorphic Factorific - alodocious!

Why is this important?

If your contact email changes, you change the AppSetting value and are done. All requests for "ContactEmail" from ConfigurationManager are Globally changed. 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:

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 :

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