Usare l'option pattern per gestire la configurazione in ASP.NET Core

di Marco De Sanctis, in ASP.NET Core,

Nello script precedente (https://www.aspitalia.com/script/1403/Reagire-Modifiche-Configurazione-ASP.NET-Core.aspx) abbiamo visto che possiamo iniettare un'istanza di IConfiguration nei nostri servizi e usarla per leggere i valori più aggiornati della configurazione.

Tuttavia, questa soluzione soffre di alcuni problemi:

  • la lettura dei dati fa uso di magic string e pertanto tende a essere un po' problematica se dobbiamo effettuare dei refactoring, oltre che prona agli errori;
  • la rilettura della configurazione avviene sì in automatico, ma in maniera incontrollata. C'è per esempio il rischio che, se la modifica dei setting avviene in concomitanza con una richiesta, due servizi di quest'ultima richiesta possano leggere due dati diversi;
  • possiamo utilizzare solo assegnazioni di tipi nativi, se invece dobbiamo configurare oggetti più complessi (per es. un delegate) la soluzione non è così banale.

Per ovviare a queste problematiche, possiamo utilizzare il cosiddetto Options Pattern.

Cerchiamo di capire di cosa si tratta.

Come prima cosa, dobbiamo creare una classe che rappresenta la parte della configurazione che vogliamo passare al nostro oggetto. Per esempio, per una sezione di appsettings.json come la seguente

{
  ...
  "option": {
    "MyStringValue": "value1"
  },
  ....
}

possiamo creare una classe simile:

public class ValueOption
{
    public string MyStringValue { get; set; }
}

A questo punto, nel nostro Startup, possiamo configurare ValueOption associandola alla relativa sezione di appSettings:

public void ConfigureServices(IServiceCollection services)
{
  // ...altro codice qui...

  services.Configure<ValueOption>(Configuration.GetSection("option"));
}

Spostiamo ora la nostra attenzione a come possiamo iniettare questa classe nei nostri componenti. Il modo più basilare è usare l'interfaccia generica IOptions<T>:

public ConfigDemoController(IOptions<ValueOption> option)
{
    _option = option;
}

[HttpGet]
public string Get()
{
    return $"{_timestamp}: {_option.Value.MyStringValue}";
}

Attenzione, però: questa interfaccia non supporta il reload della configurazione. Pertanto, è da utilizzare solo per quelle impostazioni il cui valore assumiamo essere fisso per tutto il ciclo di vita dell'applicazione.

Se invece vogliamo supportare il refresh, dobbiamo iniettare un oggetto di tipo IOptionsSnapshot<T>:

public ConfigDemoController(IOptionsSnapshot<ValueOption> option)
{
    _option = option;
}

[HttpGet]
public string Get()
{
    return $"{_timestamp}: {_option.Value.MyStringValue}";
}

Il codice è assolutamente analogo, ma al contrario del caso precedente, in questo secondo esempio vedremo il valore aggiornarsi, modificando il contenuto di appsettings.json. Un dettaglio importante è che IOptionsSnapshot<T> è registrata come Scoped. Questo vuol ci garantisce che, nel contesto di una singola richiesta, il valore dei setting resterà costante, anche se ci dovesse essere una modifica concorrente.

Il ciclo di vita Scoped ha come effetto collaterale che, ovviamente, non possiamo utilizzarla con oggetti di tipo Singleton. In un prossimo script scopriremo come ovviare a questa limitazione.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi