martes, 12 de julio de 2016
Cuando ASP.NET “clásico” modificábamos algún setting de la aplicación almacenado en el archivo web.config, esto traía como consecuencia directa el reciclado del pool, o en otras palabras, la aplicación se reiniciaba irremediablemente para recargar los nuevos valores de configuración.
Pero como sabemos, en ASP.NET Core el sistema de configuración ha cambiado para mejor, y ahora los settings podemos almacenarlos en archivos JSON independientes del resto de configuraciones del proyecto, así como en otros formatos y ubicaciones. Y ya que se ponían con el tema, el equipo de ASP.NET ha aprovechado para hacer que podamos modificar los settings sobre los archivos de configuración y que éstos sean aplicados sobre la marcha, sin necesidad de volver a arrancar la aplicación.
¿Y cómo conseguimos esto? Pues la verdad es que lo han puesto bastante sencillo, como podemos ver en la siguiente porción de código de la clase de inicialización de la aplicación:
Por ejemplo, imaginemos el siguiente contenido en el archivo de settings:
Un posible enfoque es registrar en el contenedor de una instancia singleton de la clase que represente nuestros settings, la misma que utilizamos con
El siguiente código simplificado de la clase de inicialización muestra cómo podríamos conseguirlo:
Publicado en Variable not found.
Pero como sabemos, en ASP.NET Core el sistema de configuración ha cambiado para mejor, y ahora los settings podemos almacenarlos en archivos JSON independientes del resto de configuraciones del proyecto, así como en otros formatos y ubicaciones. Y ya que se ponían con el tema, el equipo de ASP.NET ha aprovechado para hacer que podamos modificar los settings sobre los archivos de configuración y que éstos sean aplicados sobre la marcha, sin necesidad de volver a arrancar la aplicación.
¿Y cómo conseguimos esto? Pues la verdad es que lo han puesto bastante sencillo, como podemos ver en la siguiente porción de código de la clase de inicialización de la aplicación:
public class Startup { public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); // More startup code… } }¡Así de fácil! Estableciendo a cierto el valor del parámetro
reloadOnChange
, cuando leamos los settings de nuestra aplicación mediante una instancia de IConfiguration
los valores obtenidos siempre estarán frescos como lechugas :) Por ejemplo, imaginemos el siguiente contenido en el archivo de settings:
{ "Title": "Probando, probando…" }Si accedemos a los parámetros de configuración como se muestra en el siguiente código, podremos observar que las peticiones a "/settings/title" retornan el valor especificado en el archivo de configuración, y aunque lo modifiquemos durante la ejecución, al realizar de nuevo la petición aparecerá actualizado sin necesidad de echar abajo la aplicación:
public class SettingsController : Controller { private readonly IConfiguration _configuration; public SettingsController(IConfiguration configuration) { _configuration = configuration; } public IActionResult Title() { return Content(_configuration["Title"]); } }Pero ojo, esto ocurre sólo cuando accedemos a los datos de forma no tipada, mediante la instancia de
IConfiguration
. En cambio, si utilizamos utilizamos la alternativa tipada basada en IOptions<T>
, los datos no serán actualizados porque la instancia de nuestra clase de settings es cargada exclusivamente durante el arranque. Pero no os preocupéis porque seguro que podemos idear alguna forma sencilla de conseguirlo ;)Un posible enfoque es registrar en el contenedor de una instancia singleton de la clase que represente nuestros settings, la misma que utilizamos con
IOptions<T>
, y asegurarnos de que sus valores cambian cuando el archivo de settings es modificado. De esta forma, siempre que un componente necesite acceder a los settings, tendrá la versión más actual de los valores.El siguiente código simplificado de la clase de inicialización muestra cómo podríamos conseguirlo:
public class Startup { public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); // Add other settings providers Configuration = builder.Build(); // (1) Creamos una instancia singleton de la clase de settings MySettings = new MySettings() { Title = "Default title"}; // (2) Inicializar la instancia con el archivo de configuración // Ojo, requiere el paquete Microsoft.Extension.Configuration.Binders Configuration.Bind(MySettings); // (3) Aseguramos que se recarga cuando cambia el archivo var token = Configuration.GetReloadToken(); token.RegisterChangeCallback(c => { // Actualizar la instancia tras cada cambio Configuration.Bind(MySettings); }, this); } // Instancia singleton para settings tipados public MySettings MySettings { get; } // Instancia singleton para settings sin tipo public IConfigurationRoot Configuration { get; } public void ConfigureServices(IServiceCollection services) { // (4) Registramos la instancia como singleton en el DI services.AddSingleton<MySettings>(MySettings); // Configure other services here... } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { // Configure the pipeline here } } public class MySettings { public string Title {get; set; } }A grandes rasgos, lo que hacemos es:
- Creamos una instancia singleton de la clase que proveerá acceso tipado a los settings.
- La inicializamos desde el objeto de configuración mediante su extensor
Bind()
, disponible en el paquete Microsoft.Extensions.Configuration.Binders. - Obtenemos un token de recarga de la configuración y lo usamos para definir una función callback que será invocada cuando el sistema detecte cambios en ella. En dicha función actualizamos la instancia singleton de la clase tipada de settings.
- Registramos la instancia en el contenedor de inyección de dependencias.
public class SettingController : Controller { private readonly MySettings _settings; public SettingController(MySettings settings) { _settings = settings; } public IActionResult Title() { return Content(_settings.Title); } }Espero que os resulte de utilidad :)
Publicado en Variable not found.
Aún no hay comentarios, ¡sé el primero!
Enviar un nuevo comentario