Page 14 - MSDN Magazine, July 2017
P. 14
route information from within a POCO controller. Interestingly, the feature to leverage is quite general and applicable and recom- mended for a number of other common programming activities:
public class HomeController {
private IActionContextAccessor _accessor;
public HomeController(IActionContextAccessor accessor) {
_accessor = accessor;
}
... }
The constructor of the controller class can declare a parameter of type IActionContextAccessor. The interface is the key to have context information injected into the controller’s space. All that’s required to do from within the controller is to save the received instance of the interface type for later use. The following code snip- pet shows how to access the RouteData object that contains any data tokenized into the handled route URL:
public IActionResult Index() {
var controller = _accessor.ActionContext.RouteData.Values["controller"];
... }
Though possible, injecting the IActionContextAccessor service isn’t recommended because it performs poorly and is rarely needed. Another way to access route data from within a POCO controller is using the FromRoute attribute to decorate a parameter in the action:
public IActionResult Index([FromRoute] string controller) {
...
However, regardless of effectiveness, who injected a reference to IActionContextAccessor into the controller? That’s where another relevant addition to the ASP.NET Core framework fits in—the internal Inversion of Control (IoC) pattern.
Sharing Global Data
Nearly every Web application holds some data to share globally. In clas- sic ASP.NET, a common practice was to load global data at startup and save it (net of possible threading issues) into some global static variables exposed from the application object. It’s not the perfect solution for everyone, but quite functional if you’re aware of what you were doing. ASP.NET Core provides a better way to do it that guarantees that every application context receives just what it needs, thus reducing the risk of accessing unwanted data in unwanted contexts. Any chunk of global data must be added to the IoC subsystem in the ConfigureServices method. Here’s how you would add the action context, for example:
public void ConfigureServices(IServiceCollection services) {
...
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
the object globally available you just add it to the IoC system via AddSingleton. The operation takes place only once.
An Ad Hoc Framework for Application Options
As far as global data is concerned, ASP.NET Core isn’t limited to basic IoC functions, it also supports options. Options are a feature specifically designed to deal with the initial configuration of the application, namely mostly read-only data to be shared globally:
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("MyAppSettings.json", optional: true, reloadOnChange: true);
Configuration = builder.Build();
In the constructor of the startup class, the previous code sets a given JSON file as the provider of configuration data. The builder uses the provided information to prepare and return an IConfig- urationRoot object to be used to access data. You also declare the following in the startup class:
public IConfigurationRoot Configuration { get; }
Global data must be retrieved piece by piece through a query API. The options framework, instead, lets you load it into an aptly defined C# class to be passed around through the IoC system. Add the following code to the ConfigureServices method:
public void ConfigureServices(IServiceCollection services) {
services.AddOptions(); services.Configure<GlobalConfig>(Configuration.GetSection("Globals")); ...
}
In the example, GlobalConfig is a custom class you define to be 1:1 with the content of the JSON initial data. The Configure method does a good job of reflection to read the specified segment of JSON into the C# class. As a pleasant side effect of the code, any controller now can be injected options through the following pattern:
public class HomeController : Controller {
private GlobalConfig Globals { get; }
public HomeController(IOptions<GlobalConfig> config) {
Globals = config.Value; }
... }
Any global data can now be accessed via the Globals custom property.
Wrapping Up
As in the Johnson fable, with ASP.NET Core the problem isn’t the cheese and its lack thereof. There’s plenty of cheese in ASP.NET Core. The problem is the attitude to find it. Apparently, many things are dif- ferent and not easily figuring out how to do basic things such as loading globaldatamightbefrustratingatfirst.SomeASP.NETMVCcheese has been definitely moved to a different location, but is now even tastier and more abundant. Next month, I’ll touch on another relevant piece of cheese that’s not where you always get it: forms authentication. n
Dino Esposito is the author of “Microsoft .NET: Architecting Applications for the Enterprise” (Microsoft Press, 2014) and “Modern Web Applications with ASP.NET” (Microsoft Press, 2016). A technical evangelist for the .NET and Android platforms at JetBrains, and frequent speaker at industry events worldwide, Esposito shares his vision of software at software2cents@wordpress.com and on Twitter: @despos.
thanks to the following Microsoft technical expert for reviewing this article: Doug Bunting
}
}
The AddSingleton<TInterface, T> method registers that any requests for an object assignable to the TInterface type must be resolved through an instance of the concrete type T. Once the association has been mapped in the application startup, controllers will gain access to it by simply declaring a parameter of the type in their constructor.
Global information can then be read at the application startup from a variety of sources, including JSON and text files, databases, and remote services and packaged into a custom type. To make
10 msdn magazine
Cutting Edge