Condividi tramite


Gestione delle funzionalità .NET

Microsoft.FeatureManagement
Microsoft.FeatureManagement.AspNetCore

Microsoft.FeatureManagement
Microsoft.FeatureManagement.AspNetCore
Microsoft.FeatureManagement.Telemetry.ApplicationInsights
Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore

La libreria di gestione delle funzionalità .NET consente di sviluppare ed esporre funzionalità dell'applicazione in base ai flag di funzionalità. Una volta sviluppata una nuova funzionalità, molte applicazioni hanno requisiti speciali, ad esempio relativamente a quando la funzionalità deve essere abilitata e in quali condizioni. Questa libreria consente di definire queste relazioni e si integra anche in modelli di codice .NET comuni per consentire l'esposizione di queste funzionalità.

I flag di funzionalità consentono alle applicazioni .NET e ASP.NET Core di attivare o disattivare dinamicamente le funzionalità. Gli sviluppatori possono usare flag di funzionalità in casi d'uso semplici, ad esempio istruzioni condizionali, o in scenari più avanzati, ad esempio l'aggiunta condizionale di route o filtri MVC. I flag di funzionalità si basano sul sistema di configurazione .NET Core. Qualsiasi provider di configurazione .NET Core è in grado di fungere da backbone per i flag di funzionalità.

Ecco alcuni dei vantaggi dell'uso della libreria di gestione delle funzionalità .NET:

  • Una convenzione comune per la gestione delle funzionalità

  • Bassa barriera all'ingresso

    • Basato su IConfiguration
    • Supporta la configurazione del flag di funzionalità file JSON
  • Gestione della durata dei flag di funzionalità

    • I valori di configurazione possono cambiare in tempo reale; i flag di funzionalità possono essere coerenti nell'intera richiesta
  • Scenari semplici e complessi trattati

    • Attivare/disattivare le funzionalità tramite il file di configurazione dichiarativo
    • Valutare dinamicamente lo stato della funzionalità in base alla chiamata al server
  • Estensioni API per ASP.NET Core e framework MVC

    • Definizione dei percorsi di trasferimento
    • Filtri
    • Attributi azione

    La libreria di gestione delle funzionalità .NET è open source. Per altre informazioni, visitare il repository GitHub.

Flag di funzionalità

I flag di funzionalità sono costituiti da due parti, un nome e un elenco di filtri di funzionalità usati per attivare la funzionalità.

Filtri di funzionalità

I filtri di funzionalità definiscono uno scenario per il momento in cui è necessario abilitare una funzionalità. Quando viene valutato se una funzionalità è attivata o disattivata, il relativo elenco di filtri di funzionalità viene attraversato fino a quando uno dei filtri non decide che la funzionalità deve essere abilitata. A questo punto, la funzionalità viene considerata abilitata e l’attraversamento dei filtri di funzionalità si arresta. Se nessun filtro di funzionalità indica che la funzionalità deve essere abilitata, questa viene considerata disattivata.

Ad esempio, è possibile progettare un filtro di funzionalità del browser Microsoft Edge. Questo filtro di funzionalità attiverà tutte le funzionalità a cui è collegato, purché una richiesta HTTP provenga da Microsoft Edge.

Configurazione del flag di funzionalità

Il sistema di configurazione di .NET Core viene usato per determinare lo stato dei flag di funzionalità. La base di questo sistema è IConfiguration. Qualsiasi provider per IConfiguration può essere usato come provider di stato della funzionalità per la libreria dei flag di funzionalità. Questo sistema consente scenari che vanno da appsettings.json a Configurazione app di Azure e altro ancora.

Dichiarazione dei flag di funzionalità

La libreria di gestione delle funzionalità supporta appsettings.json come origine del flag di funzionalità poiché è un provider per il sistema IConfiguration di .NET Core. Di seguito è riportato un esempio del formato usato per configurare i flag di funzionalità in un file JSON.

{
    "Logging": {
        "LogLevel": {
            "Default": "Warning"
        }
    },

    // Define feature flags in a json file
    "FeatureManagement": {
        "FeatureT": {
            "EnabledFor": [
                {
                    "Name": "AlwaysOn"
                }
            ]
        },
        "FeatureU": {
            "EnabledFor": []
        },
        "FeatureV": {
            "EnabledFor": [
                {
                    "Name": "TimeWindow",
                    "Parameters": {
                        "Start": "Wed, 01 May 2019 13:59:59 GMT",
                        "End": "Mon, 01 Jul 2019 00:00:00 GMT"
                    }
                }
            ]
        }
    }
}

La sezione FeatureManagement del documento JSON viene usata per convenzione per caricare le impostazioni dei flag di funzionalità. Nella sezione precedente vengono visualizzate tre diverse funzionalità. Le funzionalità definiscono i relativi filtri di funzionalità usando la proprietà EnabledFor. Nei filtri di funzionalità per FeatureT, viene visualizzato AlwaysOn. Questo filtro di funzionalità è predefinito e, se specificato, abiliterà sempre la funzionalità. Il filtro di funzionalità AlwaysOn non richiede alcuna configurazione, quindi ha solo la proprietà Name. FeatureU non dispone di filtri nella relativa proprietà EnabledFor e pertanto non verrà mai abilitato. Tutte le funzionalità che si basano su questa funzionalità abilitata non saranno accessibili finché i filtri di funzionalità rimangono vuoti. Tuttavia, non appena viene aggiunto un filtro di funzionalità che abilita la funzionalità, può iniziare a funzionare. FeatureV specifica un filtro di funzionalità denominato TimeWindow. Questo è un esempio di filtro di funzionalità configurabile. Nell'esempio è possibile osservare che il filtro ha una proprietà Parameters. Viene usato per configurare il filtro. In questo caso, vengono configurati gli orari di inizio e fine per la funzionalità da attivare.

Lo schema dettagliato della sezione FeatureManagement è disponibile qui.

Avanzate: l'utilizzo dei due punti ':' non è consentito nei nomi dei flag di funzionalità.

Dichiarazione on/off

Il frammento di codice seguente illustra un modo alternativo per definire una funzionalità che può essere usata per le funzionalità on/off.

{
    "Logging": {
        "LogLevel": {
            "Default": "Warning"
        }
    },

    // Define feature flags in config file
    "FeatureManagement": {
        "FeatureT": true, // On feature
        "FeatureX": false // Off feature
    }
}

RequirementType

La proprietà RequirementType di un flag di funzionalità viene utilizzata per determinare se i filtri devono usare l logica Any o All durante la valutazione dello stato di una funzionalità. Se RequirementType non è specificato, il valore predefinito è Any.

  • Any significa che, per abilitare la funzionalità, è necessario che un solo filtro venga valutato come vero.
  • All indica che ogni filtro deve essere valutato come vero per abilitare la funzionalità.

Un oggetto RequirementType di All modifica l'attraversamento. In primo luogo, se non sono presenti filtri, la funzionalità è disabilitata. I filtri di funzionalità vengono quindi attraversati fino a quando uno dei filtri non decide che la funzionalità deve essere disabilitata. Se nessun filtro indica che la funzionalità deve essere disabilitata, questa viene considerata abilitata.

"FeatureW": {
    "RequirementType": "All",
    "EnabledFor": [
        {
            "Name": "TimeWindow",
            "Parameters": {
                "Start": "Mon, 01 May 2023 13:59:59 GMT",
                "End": "Sat, 01 Jul 2023 00:00:00 GMT"
            }
        },
        {
            "Name": "Percentage",
            "Parameters": {
                "Value": "50"
            }
        }
    ]
}

Nell'esempio precedente, FeatureW specifica un RequirementType di All, ovvero tutti i relativi filtri devono essere valutati come vero per abilitare la funzionalità. In questo caso, la funzionalità è abilitata per il 50% degli utenti durante l'intervallo di tempo specificato.

Schema di gestione delle funzionalità Microsoft

La libreria di gestione delle funzionalità supporta anche l'utilizzo di Microsoft Feature Management schema per dichiarare i flag di funzionalità. Questo schema è indipendente dal linguaggio ed è supportato da tutte le librerie di gestione delle funzionalità Microsoft.

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureT",
                "enabled": true,
                "conditions": {
                    "client_filters": [
                        {  
                            "name": "Microsoft.TimeWindow",
                            "parameters": {
                                "Start": "Mon, 01 May 2023 13:59:59 GMT",
                                "End": "Sat, 01 Jul 2023 00:00:00 GMT"
                            }
                        }
                    ]
                }
            }
        ]
    }
}

Nota

Se la sezione feature_management è disponibile nella configurazione, la sezione FeatureManagement viene ignorata.

La libreria di gestione delle funzionalità supporta appsettings.json come origine del flag di funzionalità poiché è un provider per il sistema IConfiguration di .NET Core. I flag di funzionalità vengono dichiarati tramite Microsoft Feature Management schema. Questo schema è indipendente dal linguaggio ed è supportato da tutte le librerie di gestione delle funzionalità Microsoft.

Di seguito è riportato un esempio di dichiarazione dei flag di funzionalità in un file JSON.

{
    "Logging": {
        "LogLevel": {
            "Default": "Warning"
        }
    },

    // Define feature flags in a json file
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureT",
                "enabled": false
            },
            {
                "id": "FeatureU",
                "enabled": true,
                "conditions": {}
            },
            {
                "id": "FeatureV",
                "enabled": true,
                "conditions": {
                    "client_filters": [
                        {  
                            "name": "Microsoft.TimeWindow",
                            "parameters": {
                                "Start": "Mon, 01 May 2023 13:59:59 GMT",
                                "End": "Sat, 01 July 2023 00:00:00 GMT"
                            }
                        }
                    ]
                }
            }
        ]
    }
}

La sezione feature_management del documento JSON viene usata per convenzione per caricare le impostazioni dei flag di funzionalità. Gli oggetti flag di funzionalità devono essere elencati nella matrice feature_flags nella sezione feature_management. Nella sezione precedente vengono fornite tre diverse funzionalità. Un flag di funzionalità ha proprietà id e enabled. id è il nome utilizzato per identificare il flag di funzionalità e farvi riferimento. La proprietà enabled specifica lo stato abilitato del flag di funzionalità. Una funzionalità è OFF se enabled è falso. Se enabled è vero, lo stato della funzionalità dipende da conditions. Se non sono presenti conditions, la funzionalità è ON. Se sono presenti conditions e vengono soddisfatti, la funzionalità è ON. Se sono presenti conditions e non vengono soddisfatti, la funzionalità è OFF. La proprietà conditions dichiara le condizioni utilizzate per abilitare dinamicamente la funzionalità. Le funzionalità definiscono i relativi filtri di funzionalità nella matrice client_filters. FeatureV specifica un filtro di funzionalità denominato Microsoft.TimeWindow. Questo è un esempio di filtro di funzionalità configurabile. Nell'esempio è possibile osservare che il filtro ha una proprietà Parameters. Viene usato per configurare il filtro. In questo caso, vengono configurati gli orari di inizio e fine per la funzionalità da attivare.

Avanzate: l'utilizzo dei due punti ':' non è consentito nei nomi dei flag di funzionalità.

RequirementType

La proprietà requirement_type di conditions viene utilizzata per determinare se i filtri devono usare la logica Any o All durante la valutazione dello stato di una funzionalità. Se requirement_type non è specificato, il valore predefinito è Any.

  • Any significa che, per abilitare la funzionalità, è necessario che un solo filtro venga valutato come vero.
  • All indica che ogni filtro deve essere valutato come vero per abilitare la funzionalità.

Un oggetto requirement_type di All modifica l'attraversamento. In primo luogo, se non è presente alcun filtro, la funzionalità verrà disabilitata. Se sono presenti filtri, i filtri di funzionalità vengono quindi attraversati fino a quando uno dei filtri non decide che la funzionalità deve essere disabilitata. Se nessun filtro indica che la funzionalità deve essere disabilitata, questa verrà considerata abilitata.

{
    "id": "FeatureW",
    "enabled": true,
    "conditions": {
        "requirement_type": "All",
        "client_filters": [
            {
                "name": "Microsoft.TimeWindow",
                "parameters": {
                    "Start": "Mon, 01 May 2023 13:59:59 GMT",
                    "End": "Sat, 01 Jul 2023 00:00:00 GMT"
                }
            },
            {
                "name": "Microsoft.Percentage",
                "parameters": {
                    "Value": "50"
                }
            }
        ]
    }
}

Nell'esempio precedente, FeatureW specifica un requirement_type di All, ovvero tutti i relativi filtri devono essere valutati come vero per abilitare la funzionalità. In questo caso, la funzionalità verrà abilitata per il 50% degli utenti durante l'intervallo di tempo specificato.

Schema di gestione delle funzionalità .NET

Nelle versioni precedenti lo schema primario per la libreria di gestione delle funzionalità era .NET feature management schema. A partire dalla versione 4.0.0, le nuove funzionalità, incluse le varianti e i dati di telemetria, non saranno supportate per lo schema di gestione delle funzionalità .NET.

Nota

Se nella configurazione è presente un flag di funzionalità scritto con Microsoft Feature Management schema, qualsiasi flag di funzionalità scritto con .NET feature management schema verrà ignorato.

Consumo

La forma di base di gestione delle funzionalità consiste nel verificare se un flag di funzionalità è abilitato e quindi eseguire azioni in base al risultato. Questa operazione viene eseguita tramite il metodo IFeatureManager di IsEnabledAsync.

…
IFeatureManager featureManager;
…
if (await featureManager.IsEnabledAsync("FeatureX"))
{
    // Do something
}

Registrazione del servizio

La gestione delle funzionalità si basa sull'inserimento delle dipendenze di .NET Core. È possibile registrare i servizi di gestione di funzionalità usando le convenzioni standard.

using Microsoft.FeatureManagement;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddFeatureManagement();
    }
}

Per impostazione predefinita, la gestione funzionalità recupera la configurazione dei flag di funzionalità dalla sezione "FeatureManagement" dei dati di configurazione di .NET Core. Se la sezione "FeatureManagement" non esiste, la configurazione viene considerata vuota.

Nota

È anche possibile specificare che la configurazione del flag di funzionalità deve essere recuperata da una sezione di configurazione diversa passando la sezione a AddFeatureManagement. L'esempio seguente indica invece la lettura da un'altra sezione chiamata "MyFeatureFlags":

services.AddFeatureManagement(configuration.GetSection("MyFeatureFlags"));

Inserimento delle dipendenze

Quando si usa la libreria di gestione delle funzionalità con MVC, è possibile ottenere IFeatureManager tramite l'inserimento delle dipendenze.

public class HomeController : Controller
{
    private readonly IFeatureManager _featureManager;
    
    public HomeController(IFeatureManager featureManager)
    {
        _featureManager = featureManager;
    }
}

Servizi di gestione delle funzionalità con ambito

Il metodo AddFeatureManagement aggiunge i servizi di gestione delle funzionalità come singleton all'interno dell'applicazione, ma esistono scenari in cui potrebbe essere necessario aggiungere i servizi di gestione delle funzionalità come servizi con ambito. Ad esempio, gli utenti possono voler usare filtri di funzionalità che utilizzano servizi con ambito per informazioni di contesto. In questo caso, è consigliabile usare il metodo AddScopedFeatureManagement. Ciò garantisce che i servizi di gestione delle funzionalità, inclusi i filtri delle funzionalità, vengano aggiunti come servizi con ambito.

services.AddScopedFeatureManagement();

Integrazione ASP.NET Core

La libreria di gestione delle funzionalità offre funzionalità in ASP.NET Core e MVC per abilitare scenari comuni di flag di funzionalità nelle applicazioni Web. Queste funzionalità sono disponibili facendo riferimento al pacchetto NuGet Microsoft.FeatureManagement.AspNetCore.

Controller e azioni

Il controller e le azioni MVC possono richiedere l'abilitazione di una determinata funzionalità, o di un qualsiasi elenco di funzionalità, per l'esecuzione. A tale scopo, è possibile usare un oggetto FeatureGateAttribute, disponibile nello spazio dei nomi Microsoft.FeatureManagement.Mvc.

[FeatureGate("FeatureX")]
public class HomeController : Controller
{
    …
}

Il codice HomeController precedente è gestito da "FeatureX". "FeatureX" deve essere abilitato prima di poter eseguire qualsiasi azione contenuta in HomeController.

[FeatureGate("FeatureX")]
public IActionResult Index()
{
    return View();
}

L'azione MVC Index precedente richiede l'abilitazione di "FeatureX" prima che possa essere eseguita.

Gestione delle azioni disabilitata

Quando un controller o un'azione MVC vengono bloccati perché nessuna delle funzionalità specificate è abilitata, verrà richiamato un oggetto registrato IDisabledFeaturesHandler. Per impostazione predefinita, viene registrato un gestore minimalista che restituisce HTTP 404. È possibile eseguire l'override usando IFeatureManagementBuilder quando si registrano flag di funzionalità.

public interface IDisabledFeaturesHandler
{
    Task HandleDisabledFeature(IEnumerable<string> features, ActionExecutingContext context);
}

Visualizza

Nelle viste MVC, è possibile usare i tag <feature> per eseguire il rendering condizionato del contenuto in base allo stato di attivazione del flag di funzionalità.

<feature name="FeatureX">
  <p>This can only be seen if 'FeatureX' is enabled.</p>
</feature>

È anche possibile negare la valutazione dell'helper tag per visualizzare il contenuto quando una funzionalità o un set di funzionalità sono disabilitate. Impostando negate="true" nell'esempio seguente, il rendering del contenuto viene eseguito solo se FeatureX è disabilitato.

<feature negate="true" name="FeatureX">
  <p>This can only be seen if 'FeatureX' is disabled.</p>
</feature>

Il tag <feature> può fare riferimento a più funzionalità specificando un elenco delimitato da virgole di funzionalità nell'attributo name.

<feature name="FeatureX,FeatureY">
  <p>This can only be seen if 'FeatureX' and 'FeatureY' are enabled.</p>
</feature>

Per impostazione predefinita, per il rendering del tag di funzionalità è necessario abilitare tutte le funzionalità elencate. Questo comportamento può essere sottoposto a override aggiungendo l'attributo requirement come illustrato nell'esempio seguente.

<feature name="FeatureX,FeatureY" requirement="Any">
  <p>This can only be seen if either 'FeatureX' or 'FeatureY' or both are enabled.</p>
</feature>

Nelle viste MVC, è possibile usare i tag <feature> per eseguire il rendering condizionato del contenuto in base allo stato di attivazione del flag di funzionalità o dell’attivazione di una variante specifica di una funzionalità. Per altre informazioni, vedere la sezione sulle varianti.

<feature name="FeatureX">
  <p>This can only be seen if 'FeatureX' is enabled.</p>
</feature>
<feature name="FeatureX" variant="Alpha">
  <p>This can only be seen if variant 'Alpha' of 'FeatureX' is assigned.</p>
</feature>

È anche possibile negare la valutazione dell'helper tag per visualizzare il contenuto quando una funzionalità o un set di funzionalità sono disabilitate. Impostando negate="true" nell'esempio seguente, il rendering del contenuto viene eseguito solo se FeatureX è disabilitato.

<feature negate="true" name="FeatureX">
  <p>This can only be seen if 'FeatureX' is disabled.</p>
</feature>
<feature negate="true" name="FeatureX" variant="Alpha">
  <p>This can only be seen if variant 'Alpha' of 'FeatureX' isn't assigned.</p>
</feature>

Il tag <feature> può fare riferimento a più funzionalità/varianti specificando un elenco delimitato da virgole di funzionalità/varianti nell'attributo name/variant.

<feature name="FeatureX,FeatureY">
  <p>This can only be seen if 'FeatureX' and 'FeatureY' are enabled.</p>
</feature>
<feature name="FeatureX" variant="Alpha,Beta">
  <p>This can only be seen if variant 'Alpha' or 'Beta' of 'FeatureX' is assigned.</p>
</feature>

Nota

Se variant è specificato, è necessario specificare una sola funzionalità.

Per impostazione predefinita, per il rendering del tag di funzionalità è necessario abilitare tutte le funzionalità elencate. Questo comportamento può essere sottoposto a override aggiungendo l'attributo requirement come illustrato nell'esempio seguente.

Nota

Se un requirement di And viene usato insieme a variant, verrà generato un errore, poiché non è mai possibile assegnare più varianti.

<feature name="FeatureX,FeatureY" requirement="Any">
  <p>This can only be seen if either 'FeatureX' or 'FeatureY' or both are enabled.</p>
</feature>

Il tag <feature> richiede il funzionamento di un helper tag. A tale scopo, aggiungere l'helper tag di gestione delle funzionalità al file ViewImports.cshtml.

@addTagHelper *, Microsoft.FeatureManagement.AspNetCore

Filtri MVC

I filtri di azione MVC possono essere configurati per l'esecuzione condizionale in base allo stato di una funzionalità. Questa operazione viene eseguita registrando i filtri MVC in modo compatibile con le funzionalità. La pipeline di gestione delle funzionalità supporta filtri di azione MVC asincroni, che implementano IAsyncActionFilter.

services.AddMvc(o => 
{
    o.Filters.AddForFeature<SomeMvcFilter>("FeatureX");
});

Il codice sopra aggiunge un filtro MVC denominato SomeMvcFilter. Questo filtro viene attivato solo all'interno della pipeline MVC se "FeatureX" è attivato.

Razor Pages

Le pagine Razor MVC possono richiedere l'abilitazione di una determinata funzionalità, o di un qualsiasi elenco di funzionalità, per l'esecuzione. A tale scopo, è possibile usare un oggetto FeatureGateAttribute, disponibile nello spazio dei nomi Microsoft.FeatureManagement.Mvc.

[FeatureGate("FeatureX")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Il codice precedente configura una pagina Razor per richiedere l'abilitazione di "FeatureX". Se la funzionalità non è abilitata, la pagina genera un risultato HTTP 404 (NotFound).

Se usato nelle pagine Razor, FeatureGateAttribute deve essere inserito nel tipo di gestore di pagina. Non può essere inserito in singoli metodi del gestore.

Compilazione dell'applicazione

La libreria di gestione delle funzionalità può essere usata per aggiungere rami dell'applicazione e middleware che vengono eseguiti in modo condizionale in base allo stato della funzionalità.

app.UseMiddlewareForFeature<ThirdPartyMiddleware>("FeatureX");

Con la chiamata precedente, l'applicazione aggiunge un componente middleware visualizzato solo nella pipeline di richiesta se la funzionalità "FeatureX" è abilitata. Se la funzionalità è abilitata/disabilitata durante il runtime, la pipeline middleware può essere modificata in modo dinamico.

Ciò produce la capacità più generica di creare rami nell'intera applicazione in base alla funzionalità.

app.UseForFeature(featureName, appBuilder => 
{
    appBuilder.UseMiddleware<T>();
});

Implementazione di un filtro di funzionalità

La creazione di un filtro di funzionalità consente di abilitare le funzionalità in base ai criteri definiti dall'utente. Per implementare un filtro di funzionalità, è necessario implementare l'interfaccia IFeatureFilter. IFeatureFilter ha un solo metodo denominato EvaluateAsync. Quando una funzionalità specifica che può essere abilitata per un filtro di funzionalità, viene chiamato il metodo EvaluateAsync. Se EvaluateAsync restituisce true, significa che la funzionalità deve essere abilitata.

Il frammento di codice seguente illustra come aggiungere un filtro di funzionalità MyCriteriaFilter personalizzato.

services.AddFeatureManagement()
        .AddFeatureFilter<MyCriteriaFilter>();

I filtri delle funzionalità vengono registrati chiamando AddFeatureFilter<T> sull'oggetto IFeatureManagementBuilder restituito da AddFeatureManagement. Questi filtri di funzionalità hanno accesso ai servizi esistenti all'interno della raccolta di servizi usata per aggiungere flag di funzionalità. L'inserimento delle dipendenze può essere usato per recuperare questi servizi.

Nota

Quando si fa riferimento ai filtri nelle impostazioni del flag di funzionalità (ad esempio, appsettings.json), la parte Filtro del nome del tipo deve essere omessa. Per altre informazioni, vedere la sezione Filter Alias Attribute.

Filtri di funzionalità con parametri

Alcuni filtri di funzionalità richiedono parametri per decidere se una funzionalità deve essere attivata o meno. Ad esempio, un filtro di funzionalità del browser può attivare una funzionalità per un determinato set di browser. Potrebbe essere necessario che i browser Edge e Chrome abilitino una funzionalità, mentre per Firefox non è così. A tale scopo, è possibile progettare un filtro di funzionalità per prevedere parametri. Questi parametri verranno specificati nella configurazione della funzionalità e nel codice sarebbero accessibili tramite il parametro FeatureFilterEvaluationContext di IFeatureFilter.EvaluateAsync.

public class FeatureFilterEvaluationContext
{
    /// <summary>
    /// The name of the feature being evaluated.
    /// </summary>
    public string FeatureName { get; set; }

    /// <summary>
    /// The settings provided for the feature filter to use when evaluating whether the feature should be enabled.
    /// </summary>
    public IConfiguration Parameters { get; set; }
}

FeatureFilterEvaluationContext ha una proprietà denominata Parameters. Questi parametri rappresentano una configurazione non elaborata che il filtro delle funzionalità può usare per decidere come valutare se la funzionalità deve essere abilitata o meno. Per usare nuovamente il filtro delle funzionalità del browser come esempio, il filtro può usare Parameters per estrarre un set di browser consentiti che verrebbero specificati per la funzionalità e quindi controllare se la richiesta viene inviata da uno di questi browser.

[FilterAlias("Browser")]
public class BrowserFilter : IFeatureFilter
{
    …

    public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
    {
        BrowserFilterSettings settings = context.Parameters.Get<BrowserFilterSettings>() ?? new BrowserFilterSettings();

        //
        // Here we would use the settings and see if the request was sent from any of BrowserFilterSettings.AllowedBrowsers
    }
}

Attributo alias filtro

Quando un filtro di funzionalità viene registrato per un flag di funzionalità, l'alias usato nella configurazione è il nome del tipo di filtro delle funzionalità con il suffisso Filter, se presente, rimosso. Ad esempio, si farà riferimento a MyCriteriaFilter come MyCriteria nella configurazione.

"MyFeature": {
    "EnabledFor": [
        {
            "Name": "MyCriteria"
        }
    ]
}

Questo comportamento può essere sottoposto a override tramite FilterAliasAttribute. Un filtro di funzionalità può essere decorato con questo attributo per dichiarare il nome che deve essere usato nella configurazione per fare riferimento a questo filtro di funzionalità all'interno di un flag di funzionalità.

Filtri delle funzionalità mancanti

Se una funzionalità è configurata per essere abilitata per un filtro di funzionalità specifico e tale filtro di funzionalità non viene registrato, viene generata un'eccezione quando viene valutata la funzionalità. L'eccezione può essere disabilitata usando le opzioni di gestione delle funzionalità.

services.Configure<FeatureManagementOptions>(options =>
{
    options.IgnoreMissingFeatureFilters = true;
});

Uso di HttpContext

I filtri di funzionalità possono valutare se una funzionalità deve essere abilitata in base alle proprietà di una richiesta HTTP. Questa operazione viene eseguita esaminando il contesto HTTP. Un filtro di funzionalità può ottenere un riferimento al contesto HTTP ottenendo un oggetto IHttpContextAccessor tramite l'inserimento delle dipendenze.

public class BrowserFilter : IFeatureFilter
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public BrowserFilter(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
    }
}

IHttpContextAccessor deve essere aggiunto al contenitore di inserimento delle dipendenze all'avvio perché sia disponibile. Può essere registrato in IServiceCollection utilizzando il metodo seguente.

public void ConfigureServices(IServiceCollection services)
{
    …
    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    …
}

Avanzate: IHttpContextAccessor/HttpContext non deve essere usato nei componenti Razor delle app Blazor sul lato server. L'approccio consigliato per passare il contesto HTTP nelle app Blazor consiste nel copiare i dati in un servizio con ambito. Per le app Blazor, AddScopedFeatureManagement deve essere usato per registrare i servizi di gestione delle funzionalità. Per altre informazioni, vedere la sezione Scoped Feature Management Services.

Fornire un contesto per la valutazione delle funzionalità

Nelle applicazioni console non esiste alcun contesto ambientale, ad esempio HttpContext, che i filtri di funzionalità possono acquisire e usare per verificare se una funzionalità deve essere attivata o disattivata. In questo caso, le applicazioni devono fornire un oggetto che rappresenta un contesto nel sistema di gestione delle funzionalità per l'uso da parte dei filtri delle funzionalità. A tale scopo, viene utilizzata la funzione IFeatureManager.IsEnabledAsync<TContext>(string featureName, TContext appContext). L'oggetto appContext fornito alla gestione funzionalità può essere usato dai filtri delle funzionalità per valutare lo stato di una funzionalità.

MyAppContext context = new MyAppContext
{
    AccountId = current.Id;
}

if (await featureManager.IsEnabledAsync(feature, context))
{
…
}

Filtri delle funzionalità contestuali

I filtri delle funzionalità contestuali implementano l'interfaccia IContextualFeatureFilter<TContext>. Questi filtri di funzionalità speciali possono sfruttare il contesto passato quando viene chiamato IFeatureManager.IsEnabledAsync<TContext>. Il parametro di tipo TContext in IContextualFeatureFilter<TContext> descrive il tipo di contesto che il filtro è in grado di gestire. Ciò consente allo sviluppatore di un filtro di funzionalità contestuale di descrivere ciò che è necessario per coloro che desiderano usarlo. Poiché ogni tipo è un discendente dell'oggetto, è possibile chiamare un filtro che implementi IContextualFeatureFilter<object> per qualsiasi contesto specificato. Per illustrare un esempio di filtro di funzionalità contestuale più specifico, prendere in considerazione una funzionalità abilitata se un account si trova in un elenco configurato di account abilitati.

public interface IAccountContext
{
    string AccountId { get; set; }
}

[FilterAlias("AccountId")]
class AccountIdFilter : IContextualFeatureFilter<IAccountContext>
{
    public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext featureEvaluationContext, IAccountContext accountId)
    {
        //
        // Evaluate if the feature should be on with the help of the provided IAccountContext
    }
}

È possibile notare che AccountIdFilter richiede che sia disponibile un oggetto che implementa IAccountContext per poter valutare lo stato di una funzionalità. Quando si usa questo filtro di funzionalità, il chiamante deve assicurarsi che l'oggetto passato implementi IAccountContext.

Nota

È possibile implementare un'unica interfaccia di filtro funzionalità solo da un singolo tipo. Se si tenta di aggiungere un filtro di funzionalità che implementa più di un'unica interfaccia filtro funzionalità, viene restituito un oggetto ArgumentException.

Uso di filtri contestuali e non contestuali con lo stesso alias

I filtri di IFeatureFilter e IContextualFeatureFilter possono condividere lo stesso alias. In particolare, è possibile avere un alias di filtro condiviso da 0 o 1 IFeatureFilter e 0 o N IContextualFeatureFilter<ContextType>, purché sia presente al massimo un filtro applicabile per ContextType.

Il passaggio seguente descrive il processo di selezione di un filtro quando i filtri contestuali e non contestuali con lo stesso nome vengono registrati in un'applicazione.

Si supponga di avere un filtro non contestuale denominato FilterA e due filtri contestuali FilterB e FilterC che accettano rispettivamente contesti TypeB e TypeC. Tutti e tre i filtri condividono lo stesso alias SharedFilterName.

È disponibile anche un flag di funzionalità MyFeature che usa il filtro di funzionalità SharedFilterName nella relativa configurazione.

Se vengono registrati tutti e tre i filtri:

  • Quando si chiama IsEnabledAsync("MyFeature"), viene usato FilterA per valutare il flag di funzionalità.
  • Quando si chiama IsEnabledAsync("MyFeature", context), se il tipo di contesto è TypeB, viene usato FilterB. Se il tipo di contesto è TypeC, viene usato FilterC.
  • Quando si chiama IsEnabledAsync("MyFeature", context), se il tipo di contesto è TypeF, viene usato FilterA.

Filtri di funzionalità predefiniti

Esistono alcuni filtri di funzionalità disponibili con il pacchetto Microsoft.FeatureManagement: PercentageFilter, TimeWindowFilter, ContextualTargetingFilter e TargetingFilter. Tutti i filtri, ad eccezione di TargetingFilter, vengono aggiunti automaticamente quando la gestione delle funzionalità viene registrata dal metodo AddFeatureManagement. TargetingFilter viene aggiunto con il metodo WithTargeting descritto in dettaglio nella sezione Targeting seguente.

Ognuno dei filtri di funzionalità predefiniti ha i propri parametri. Ecco l'elenco dei filtri delle funzionalità insieme agli esempi.

Microsoft.Percentage

Questo filtro offre la possibilità di abilitare una funzionalità in base a una percentuale impostata.

"EnhancedPipeline": {
    "EnabledFor": [
        {
            "Name": "Microsoft.Percentage",
            "Parameters": {
                "Value": 50
            }
        }
    ]
}

Microsoft.TimeWindow

Questo filtro offre la possibilità di abilitare una funzionalità in base a una finestra temporale. Se viene specificato solo End, la funzionalità viene considerata fino a quel momento. Se viene specificato solo Start, la funzionalità viene considerata in tutti i punti dopo tale periodo.

"EnhancedPipeline": {
    "EnabledFor": [
        {
            "Name": "Microsoft.TimeWindow",
            "Parameters": {
                "Start": "Wed, 01 May 2019 13:59:59 GMT",
                "End": "Mon, 01 Jul 2019 00:00:00 GMT"
            }
        }
    ]
}

L'intervallo di tempo può essere configurato per la ricorsione periodica. Può essere utile per gli scenari in cui potrebbe essere necessario attivare una funzionalità durante un periodo di traffico basso o elevato di un giorno o di determinati giorni di una settimana. Per espandere la singola finestra temporale in intervalli di tempo ricorrenti, è necessario specificare la regola di ricorrenza nel parametro Recurrence.

Nota

Start e End devono essere entrambi specificati per abilitare Recurrence.

"EnhancedPipeline": {
    "EnabledFor": [
        {
            "Name": "Microsoft.TimeWindow",
            "Parameters": {
                "Start": "Fri, 22 Mar 2024 20:00:00 GMT",
                "End": "Sat, 23 Mar 2024 02:00:00 GMT",
                "Recurrence": {
                    "Pattern": {
                        "Type": "Daily",
                        "Interval": 1
                    },
                    "Range": {
                        "Type": "NoEnd"
                    }
                }
            }
        }
    ]
}

Le impostazioni Recurrence sono costituite da due parti: Pattern (con quale frequenza si ripete l'intervallo di tempo) e Range (per quanto tempo il criterio di ricorrenza viene ripetuto).

Criterio ricorrenza

Esistono due possibili tipi di criteri di ricorrenza: Daily e Weekly. Ad esempio, un intervallo di tempo potrebbe ripetere "ogni giorno", "ogni tre giorni", "ogni lunedì" o "ogni altro venerdì".

A seconda del tipo, alcuni campi di Pattern sono obbligatori, facoltativi o ignorati.

  • Daily

    Il criterio di ricorrenza giornaliera determina la ripetizione dell'intervallo di tempo in base a un numero di giorni tra ogni occorrenza.

    Proprietà Pertinenza Descrizione
    Tipo Obbligatorio Deve essere impostato su Daily.
    Intervallo Facoltativo Specifica il numero di giorni tra ciascuna occorrenza. Il valore predefinito è 1.
  • Weekly

    Il criterio di ricorrenza settimanale fa sì che l'intervallo di tempo si ripeta nello stesso giorno o negli stessi giorni della settimana, in base al numero di settimane tra ogni set di occorrenze.

    Proprietà Pertinenza Descrizione
    Tipo Obbligatorio Deve essere impostato su Weekly.
    DaysOfWeek Richiesto Specifica in quali giorni della settimana si verifica l'evento.
    Intervallo Facoltativo Specifica il numero di settimane tra ciascuna serie di occorrenze. Il valore predefinito è 1.
    FirstDayOfWeek Facoltativo Specifica quale giorno viene considerato il primo giorno della settimana. Il valore predefinito è Sunday.

    L'esempio seguente ripete l'intervallo di tempo ogni lunedì e martedì

    "Pattern": {
        "Type": "Weekly",
        "Interval": 2,
        "DaysOfWeek": ["Monday", "Tuesday"]
    }
    

Nota

Start deve essere una prima occorrenza valida che si adatta al criterio di ricorrenza. Inoltre, la durata dell'intervallo di tempo non può essere più lunga della frequenza con cui si verifica. Ad esempio, non è valido avere un intervallo di 25 ore ogni giorno.

Intervallo ricorrenza

Esistono tre possibili tipi di intervallo di ricorrenza: NoEnd, EndDate e Numbered.

  • NoEnd

    L'intervallo NoEnd fa sì che la ricorrenza venga eseguita per un periodo illimitato.

    Proprietà Pertinenza Descrizione
    Tipo Obbligatorio Deve essere impostato su NoEnd.
  • EndDate

    L'intervallo EndDate fa sì che l'intervallo di tempo si verifichi in tutti i giorni che soddisfano il modello applicabile fino alla data di fine.

    Proprietà Pertinenza Descrizione
    Tipo Obbligatorio Deve essere impostato su EndDate.
    EndDate Richiesto Specifica l'ora della data in cui interrompere l'applicazione del modello. Fino a quando l'ora di inizio dell'ultima occorrenza cade prima della data di fine, l'ora di fine di tale occorrenza può essere estesa.

    L'esempio seguente ripeterà l'intervallo di tempo ogni giorno fino all'ultima occorrenza del 1° aprile 2024.

    "Start": "Fri, 22 Mar 2024 18:00:00 GMT",
    "End": "Fri, 22 Mar 2024 20:00:00 GMT",
    "Recurrence":{
        "Pattern": {
            "Type": "Daily",
            "Interval": 1
        },
        "Range": {
            "Type": "EndDate",
            "EndDate": "Mon, 1 Apr 2024 20:00:00 GMT"
        }
    }
    
  • Numbered

    L'intervallo Numbered fa sì che l'intervallo di tempo si verifichi per un numero fisso di volte (in base al modello).

    Proprietà Pertinenza Descrizione
    Tipo Obbligatorio Deve essere impostato su Numbered.
    NumberOfOccurrences Richiesto Specifica il numero di occorrenze.

    L'esempio seguente ripeterà l'intervallo di tempo lunedì e martedì fino a quando non saranno presenti tre occorrenze, che si verificano rispettivamente il 1° aprile(lun), il 2 aprile(mar) e l'8 aprile (lun).

    "Start": "Mon, 1 Apr 2024 18:00:00 GMT",
    "End": "Mon, 1 Apr 2024 20:00:00 GMT",
    "Recurrence":{
        "Pattern": {
            "Type": "Weekly",
            "Interval": 1,
            "DaysOfWeek": ["Monday", "Tuesday"]
        },
        "Range": {
            "Type": "Numbered",
            "NumberOfOccurrences": 3
        }
    }
    

Per creare una regola di ricorrenza, è necessario specificare sia Pattern che Range. Qualsiasi tipo di criterio può funzionare con qualsiasi tipo di intervallo.

Avanzate: l'offset del fuso orario della proprietà Start viene applicato alle impostazioni di ricorrenza.

Microsoft.Targeting

Questo filtro offre la possibilità di abilitare una funzionalità per un pubblico di destinazione. Una spiegazione approfondita della destinazione è illustrata nella sezione di destinazione seguente. I parametri di filtro includono un oggetto Audience che descrive utenti, gruppi, utenti/gruppi esclusi e una percentuale predefinita della base di utenti che deve avere accesso alla funzionalità. Ogni oggetto gruppo elencato nella sezione Groups deve specificare anche la percentuale di accesso ai membri del gruppo. Se nella sezione Exclusion viene specificato un utente, sia direttamente che se l'utente si trova in un gruppo escluso, la funzionalità è disabilitata. In caso contrario, se un utente viene specificato direttamente nella sezione Users, se l'utente si trova nella percentuale di implementazione inclusa di uno dei gruppi o se rientra nella percentuale di implementazione predefinita, tale utente avrà la funzionalità abilitata.

"EnhancedPipeline": {
    "EnabledFor": [
        {
            "Name": "Microsoft.Targeting",
            "Parameters": {
                "Audience": {
                    "Users": [
                        "Jeff",
                        "Alicia"
                    ],
                    "Groups": [
                        {
                            "Name": "Ring0",
                            "RolloutPercentage": 100
                        },
                        {
                            "Name": "Ring1",
                            "RolloutPercentage": 50
                        }
                    ],
                    "DefaultRolloutPercentage": 20,
                    "Exclusion": {
                        "Users": [
                            "Ross"
                        ],
                        "Groups": [
                            "Ring2"
                        ]
                    }
                }
            }
        }
    ]
}

Spazi dei nomi alias del filtro di funzionalità

Tutti gli alias di filtro di funzionalità predefiniti si trovano nello spazio dei nomi del filtro di funzionalità Microsoft. Ciò consente di evitare conflitti con altri filtri di funzionalità che possono condividere lo stesso alias. I segmenti di uno spazio dei nomi dei filtri di funzionalità vengono suddivisi in base al carattere '.'. È possibile fare riferimento a un filtro di funzionalità tramite il relativo alias completo, ad esempio Microsoft.Percentage o dall'ultimo segmento che, nel caso di Microsoft.Percentage, è Percentage.

Selezione della destinazione

La destinazione è una strategia di gestione delle funzionalità che consente agli sviluppatori di implementare progressivamente nuove funzionalità alla base degli utenti. La strategia si basa sul concetto di destinazione di un set di utenti noto come gruppo di destinatari. Un gruppo di destinatari è costituito da utenti, gruppi specifici, utenti/gruppi esclusi e una percentuale designata dell'intera base di utenti. I gruppi inclusi nel gruppo di destinatari possono essere suddivisi ulteriormente in percentuali dei membri totali.

I passaggi seguenti illustrano un esempio di implementazione progressiva per una nuova funzionalità "Beta":

  1. Ai singoli utenti Jeff e Alicia viene concesso l'accesso alla versione Beta
  2. Un altro utente, Mark, chiede di acconsentire esplicitamente ed è incluso.
  3. Il venti percento di un gruppo noto come utenti "Ring1" è incluso nella Beta.
  4. Il numero di utenti "Ring1" inclusi nella Beta è salito fino al 100%.
  5. Il 5% della base di utenti è incluso nella versione Beta.
  6. La percentuale di implementazione viene incrementata fino al 100% e la funzionalità viene implementata completamente.

Questa strategia per l'implementazione di una funzionalità è incorporata nella libreria tramite il filtro di funzionalità Microsoft.Targeting incluso.

Destinazione in un'applicazione Web

Un'applicazione Web di esempio che usa il filtro di funzionalità di destinazione è disponibile nel progetto di esempio FeatureFlagDemo.

Per iniziare a usare TargetingFilter in un'applicazione, è necessario aggiungerlo alla raccolta di servizi dell'applicazione esattamente come qualsiasi altro filtro di funzionalità. A differenza di altri filtri predefiniti, TargetingFilter si basa su un altro servizio da aggiungere alla raccolta di servizi dell'applicazione. Tale servizio è un oggetto ITargetingContextAccessor.

Il tipo di implementazione usato per il servizio ITargetingContextAccessor deve essere implementato dall'applicazione che usa il filtro di destinazione. Ecco un esempio di configurazione della gestione delle funzionalità in un'applicazione Web per usare TargetingFilter con un'implementazione di ITargetingContextAccessor denominata HttpContextTargetingContextAccessor.

services.AddFeatureManagement()
        .WithTargeting<HttpContextTargetingContextAccessor>();

La funzione di accesso al contesto di destinazione e TargetingFilter vengono registrati chiamando WithTargeting<T> su IFeatureManagementBuilder.

ITargetingContextAccessor

Per usare TargetingFilter in un'applicazione Web, è necessaria un'implementazione di ITargetingContextAccessor. Ciò avviene perché quando viene eseguita una valutazione di destinazione, sono necessarie informazioni quali l'utente attualmente valutato. Queste informazioni sono note come contesto di destinazione. Applicazioni Web diverse possono estrarre queste informazioni da posizioni diverse. Alcuni esempi comuni di dove un'applicazione può eseguire il pull del contesto di destinazione sono il contesto HTTP della richiesta o un database.

Un esempio che estrae informazioni sul contesto di destinazione dal contesto HTTP dell'applicazione è incluso nel progetto di esempio FeatureFlagDemo. Questo metodo si basa sull'uso di IHttpContextAccessor, illustrato qui.

Destinazione in un'applicazione console

Il filtro di destinazione si basa su un contesto di destinazione per valutare se una funzionalità deve essere attivata. Questo contesto di destinazione contiene informazioni quali l'utente attualmente in fase di valutazione e i gruppi in cui si trova l'utente. Nelle applicazioni console, in genere non è disponibile alcun contesto ambientale per il flusso di queste informazioni nel filtro di destinazione, pertanto deve essere passato direttamente quando viene chiamato FeatureManager.IsEnabledAsync. Questa funzionalità è supportata tramite ContextualTargetingFilter. Le applicazioni che devono spostare il contesto di destinazione nel gestore delle funzionalità devono usare questa opzione anziché TargetingFilter.

Poiché ContextualTargetingFilter è IContextualTargetingFilter<ITargetingContext>, un'implementazione di ITargetingContext deve essere passata a IFeatureManager.IsEnabledAsync per poter valutare e attivare una funzionalità.

IFeatureManager fm;
…
// userId and groups defined somewhere earlier in application
TargetingContext targetingContext = new TargetingContext
{
   UserId = userId,
   Groups = groups
};

await fm.IsEnabledAsync(featureName, targetingContext);

ContextualTargetingFilter usa ancora l'alias di filtro di funzionalità Microsoft.Targeting, quindi la configurazione per questo filtro è coerente con quanto indicato in tale sezione.

Un esempio che usa ContextualTargetingFilter in un'applicazione console è disponibile nel progetto di esempio TargetingConsoleApp.

Opzioni di valutazione di destinazione

Le opzioni sono disponibili per personalizzare la modalità di esecuzione della valutazione della destinazione in tutte le funzionalità. Queste opzioni possono essere configurate durante la configurazione della gestione delle funzionalità.

services.Configure<TargetingEvaluationOptions>(options =>
{
    options.IgnoreCase = true;
});

Esclusione di destinazione

Quando si definisce un gruppo di destinatari, gli utenti e i gruppi possono essere esclusi dal gruppo di destinatari. Ciò è utile quando viene implementata una funzionalità a un gruppo di utenti, ma è necessario escludere alcuni utenti o gruppi dall'implementazione. L'esclusione viene definita aggiungendo un elenco di utenti e gruppi alla proprietà Exclusion del gruppo di destinatari.

"Audience": {
    "Users": [
        "Jeff",
        "Alicia"
    ],
    "Groups": [
        {
            "Name": "Ring0",
            "RolloutPercentage": 100
        }
    ],
    "DefaultRolloutPercentage": 0
    "Exclusion": {
        "Users": [
            "Mark"
        ]
    }
}

Nell'esempio precedente la funzionalità è abilitata per gli utenti denominati Jeff e Alicia. È anche abilitata per gli utenti nel gruppo denominato Ring0. Tuttavia, se l'utente è denominato Mark, la funzionalità è disabilitata, indipendentemente dal fatto che si trovino nel gruppo Ring0 o meno. Le esclusioni hanno la priorità rispetto al resto del filtro di destinazione.

Varianti

Quando vengono aggiunte nuove funzionalità a un'applicazione, potrebbe verificarsi un momento in cui una funzionalità include diverse opzioni di progettazione proposte. Una soluzione comune per decidere una progettazione è una forma di test A/B, che implica la fornitura di una versione diversa della funzionalità a diversi segmenti della base di utenti e la scelta di una versione in base all'interazione dell'utente. In questa libreria, la funzionalità è abilitata rappresentando diverse configurazioni di una funzionalità con varianti.

Le varianti consentono a un flag di funzionalità di diventare più di un semplice flag on/off. Una variante rappresenta un valore di un flag di funzionalità che può essere una stringa, un numero, un valore booleano o anche un oggetto di configurazione. Un flag di funzionalità che dichiara le varianti deve definire in quali circostanze deve essere usata ogni variante, descritta in modo più dettagliato nella sezione Allocazione varianti.

public class Variant
{
    /// <summary>
    /// The name of the variant.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// The configuration of the variant.
    /// </summary>
    public IConfigurationSection Configuration { get; set; }
}

Recupero di varianti

Per ogni funzionalità, è possibile recuperare una variante usando il metodo IVariantFeatureManager del GetVariantAsync.

…
IVariantFeatureManager featureManager;
…
Variant variant = await featureManager.GetVariantAsync(MyFeatureFlags.FeatureU, CancellationToken.None);

IConfigurationSection variantConfiguration = variant.Configuration;

// Do something with the resulting variant and its configuration

Dopo aver recuperato una variante, la configurazione di una variante può essere usata direttamente come oggetto IConfigurationSection dalla proprietà Configuration della variante. Un'altra opzione consiste nell'associare la configurazione a un oggetto usando il modello di associazione di configurazione di .NET.

IConfigurationSection variantConfiguration = variant.Configuration;

MyFeatureSettings settings = new MyFeatureSettings();

variantConfiguration.Bind(settings);

La variante restituita dipende dall'utente attualmente in fase di valutazione e le informazioni vengono ottenute da un'istanza di TargetingContext. Questo contesto può essere passato durante la chiamata GetVariantAsync o può essere recuperato automaticamente da un'implementazione di ITargetingContextAccessor se ne è registrato uno.

Dichiarazione dei flag di funzionalità delle varianti

Rispetto ai normali flag di funzionalità, i flag di funzionalità delle varianti hanno due proprietà aggiuntive: variants e allocation. La proprietà variants è una matrice che contiene le varianti definite per questa funzionalità. La proprietà allocation definisce la modalità di allocazione di queste varianti per la funzionalità. Come per dichiarare i normali flag di funzionalità, è possibile configurare flag di funzionalità delle varianti in un file JSON. Di seguito è riportato un esempio di flag di funzionalità delle varianti.

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyVariantFeatureFlag",
                "enabled": true,
                "allocation": {
                    "default_when_enabled": "Small",
                    "group": [
                        {
                            "variant": "Big",
                            "groups": [
                                "Ring1"
                            ]
                        }
                    ]
                },
                "variants": [
                    { 
                        "name": "Big"
                    },  
                    { 
                        "name": "Small"
                    } 
                ]
            }
        ]
    }
}

Definizione di varianti

Ogni variante ha due proprietà: un nome e una configurazione. Il nome viene usato per fare riferimento a una variante specifica e la configurazione è il valore di tale variante. La configurazione può essere impostata usando le proprietà configuration_reference o configuration_value. configuration_reference è un percorso stringa che fa riferimento a una sezione della configurazione corrente che contiene la dichiarazione del flag di funzionalità. configuration_value è una configurazione inline che può essere una stringa, un numero, un valore booleano o un oggetto di configurazione. Se vengono specificati entrambi, viene utilizzato configuration_value. Se nessuno dei due valori viene specificato, la proprietà Configuration della variante restituita sarà Null.

Un elenco di tutte le possibili varianti viene definito per ogni funzionalità nella proprietà variants.

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyVariantFeatureFlag",
                "variants": [
                    { 
                        "name": "Big", 
                        "configuration_reference": "ShoppingCart:Big" 
                    },  
                    { 
                        "name": "Small", 
                        "configuration_value": {
                            "Size": 300
                        }
                    } 
                ]
            }
        ]
    },

    "ShoppingCart": {
        "Big": {
            "Size": 600,
            "Color": "green"
        },
        "Small": {
            "Size": 300,
            "Color": "gray"
        }
    }
}

Allocazione di varianti

Il processo di allocazione delle varianti di una funzionalità è determinato dalla proprietà allocation della funzionalità.

"allocation": { 
    "default_when_enabled": "Small", 
    "default_when_disabled": "Small",  
    "user": [ 
        { 
            "variant": "Big", 
            "users": [ 
                "Marsha" 
            ] 
        } 
    ], 
    "group": [ 
        { 
            "variant": "Big", 
            "groups": [ 
                "Ring1" 
            ] 
        } 
    ],
    "percentile": [ 
        { 
            "variant": "Big", 
            "from": 0, 
            "to": 10 
        } 
    ], 
    "seed": "13973240" 
},
"variants": [
    { 
        "name": "Big", 
        "configuration_reference": "ShoppingCart:Big" 
    },  
    { 
        "name": "Small", 
        "configuration_value": "300px"
    } 
]

L'impostazione allocation di una funzionalità ha le proprietà seguenti:

Proprietà Descrizione
default_when_disabled Specifica quale variante deve essere utilizzata quando viene richiesta una variante mentre la funzionalità viene considerata disabilitata.
default_when_enabled Specifica quale variante deve essere utilizzata quando viene richiesta una variante mentre la funzionalità viene considerata abilitata e nessun'altra variante è stata assegnata all'utente.
user Specifica una variante e un elenco di utenti a cui deve essere assegnata tale variante.
group Specifica una variante e un elenco di gruppi. La variante verrà assegnata se l'utente si trova in almeno uno dei gruppi.
percentile Specifica una variante e un intervallo percentuale in base al quale deve essere assegnata la percentuale calcolata dell'utente.
seed Valore su cui si basano i calcoli percentuali per percentile. Il calcolo percentuale per un utente specifico sarà lo stesso in tutte le funzionalità se viene usato lo stesso valore seed. Se non viene specificato alcun valore seed, viene creato un valore di inizializzazione predefinito in base al nome della funzionalità.

Nell'esempio precedente, se la funzionalità non è abilitata, il gestore delle funzionalità assegnerà la variante contrassegnata come default_when_disabled all'utente corrente, in questo caso Small.

Se la funzionalità è abilitata, il gestore delle funzionalità verificherà le allocazioni user, group e percentile in tale ordine al fine di assegnare una variante. Per questo particolare esempio, se l'utente valutato è denominato Marsha nel gruppo denominato Ring1, o se l'utente si trova tra lo 0 e il 10° percentile, la variante specificata viene assegnata all'utente. In questo caso, tutti questi valori restituirebbero la variante Big. Se nessuna di queste allocazioni corrisponde, all'utente viene assegnata la variante default_when_enabled, ovvero Small.

La logica di allocazione è simile al filtro di funzionalità Microsoft.Targeting, ma esistono alcuni parametri presenti nella destinazione che non sono nell'allocazione e viceversa. I risultati della destinazione e dell'allocazione non sono correlati.

Nota

Per consentire l'allocazione delle varianti di funzionalità, è necessario registrare ITargetingContextAccessor. Questa operazione può essere eseguita chiamando il metodo WithTargeting<T>.

Override dello stato abilitato con una variante

È possibile usare varianti per eseguire l'override dello stato abilitato di un flag di funzionalità. In questo modo, le varianti possono estendere la valutazione di un flag di funzionalità. Se un chiamante controlla se è abilitato un flag con varianti, il gestore delle funzionalità verificherà se la variante assegnata all'utente corrente è configurata per eseguire l'override del risultato. Questa operazione viene eseguita usando la proprietà variante status_override facoltativa. Per impostazione predefinita, questa proprietà è impostata su None, il che significa che la variante non influisce sul fatto che il flag sia considerato abilitato o disabilitato. L'impostazione status_override su Enabled consente alla variante, se scelta, di eseguire l'override di un flag da abilitare. L'impostazione status_override su Disabled fornisce la funzionalità opposta, disabilitando quindi il flag quando viene scelta la variante. Non è possibile eseguire l'override di una funzionalità con un Status di Disabled.

Se si usa un flag di funzionalità con varianti binarie, la proprietà status_override può essere molto utile. Consente di continuare a usare API come IsEnabledAsync e FeatureGateAttribute nell'applicazione, sfruttando al tempo stesso le nuove funzionalità fornite con varianti, ad esempio allocazione percentile e seeding.

{
    "id": "MyVariantFeatureFlag",
    "enabled": true,
    "allocation": {
        "percentile": [
            {
                "variant": "On",
                "from": 10,
                "to": 20
            }
        ],
        "default_when_enabled":  "Off",
        "seed": "Enhanced-Feature-Group"
    },
    "variants": [
        {
            "name": "On"
        },
        {
            "name": "Off",
            "status_override": "Disabled"
        }
    ]
}

Nell'esempio precedente la funzionalità è sempre abilitata. Se l'utente corrente si trova nell'intervallo percentile calcolato compreso tra 10 e 20, viene restituita la variante On. In caso contrario, viene restituita la variante Off e, poiché status_override è uguale a Disabled, la funzionalità verrà ora considerata disabilitata.

Varianti nell'inserimento delle dipendenze

I flag di funzionalità varianti possono essere usati insieme all'inserimento delle dipendenze per visualizzare implementazioni diverse di un servizio per utenti diversi. A tale scopo, usare l’interfaccia IVariantServiceProvider<TService>.

IVariantServiceProvider<IAlgorithm> algorithmServiceProvider;
...

IAlgorithm forecastAlgorithm = await algorithmServiceProvider.GetServiceAsync(cancellationToken); 

Nel frammento di codice precedente, IVariantServiceProvider<IAlgorithm> recupera un'implementazione di IAlgorithm dal contenitore di inserimento delle dipendenze. L'implementazione scelta dipende da:

  • Flag di funzionalità con cui è stato registrato il servizio IAlgorithm.
  • Variante allocata per tale funzionalità.

L'oggetto IVariantServiceProvider<T> viene reso disponibile per l'applicazione chiamando IFeatureManagementBuilder.WithVariantService<T>(string featureName). Per un esempio, vedere di seguito.

services.AddFeatureManagement() 
        .WithVariantService<IAlgorithm>("ForecastAlgorithm");

La chiamata precedente rende IVariantServiceProvider<IAlgorithm> disponibile nella raccolta di servizi. Le implementazioni di IAlgorithm devono essere aggiunte separatamente tramite un metodo di aggiunta, ad esempio services.AddSingleton<IAlgorithm, SomeImplementation>(). L'implementazione di IAlgorithm, che IVariantServiceProvider usa, dipende dal flag di funzionalità varianti ForecastAlgorithm. Se non viene aggiunta alcuna implementazione di IAlgorithm alla raccolta di servizi, IVariantServiceProvider<IAlgorithm>.GetServiceAsync() restituisce un'attività con risultato Null.

{
    // The example variant feature flag
    "id": "ForecastAlgorithm",
    "enabled": true,
    "variants": [
        { 
            "Name": "AlgorithmBeta" 
        },
        ...
    ] 
}

Attributo alias del servizio delle varianti

[VariantServiceAlias("Beta")]
public class AlgorithmBeta : IAlgorithm
{
    ...
}

Il provider di servizi delle varianti userà i nomi dei tipi di implementazioni per trovare la corrispondenza con la variante allocata. Se un servizio delle varianti è decorato con VariantServiceAliasAttribute, il nome dichiarato in questo attributo deve essere usato nella configurazione per fare riferimento a questo servizio variante.

Telemetria

Quando viene distribuita una modifica del flag di funzionalità, è spesso importante analizzarne l'effetto su un'applicazione. Ecco alcune domande che possono verificarsi, ad esempio:

  • I flag sono abilitati/disabilitati come previsto?
  • Gli utenti di destinazione ottengono l'accesso a una determinata funzionalità come previsto?
  • Quale variante viene visualizzata da un utente specifico?

Questi tipi di domande possono essere risposte tramite l'emissione e l'analisi degli eventi di valutazione dei flag di funzionalità. Questa libreria supporta l'emissione di questi eventi tramite editori di telemetria. È possibile registrare uno o più server di pubblicazione di telemetria per pubblicare eventi ogni volta che vengono valutati i flag di funzionalità.

Abilitazione della telemetria

Per impostazione predefinita, i flag di funzionalità non hanno dati di telemetria generati. Per pubblicare i dati di telemetria per un determinato flag di funzionalità, il flag MUST dichiara che è abilitato per l'emissione di dati di telemetria.

Per i flag di funzionalità definiti in appsettings.json, questa operazione viene eseguita usando la proprietà telemetry.

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyFeatureFlag",
                "enabled": true,
                "telemetry": {
                    "enabled": true
                }
            }
        ]
    }
}

Il frammento di codice appsettings precedente definisce un flag di funzionalità denominato MyFeatureFlag abilitato per i dati di telemetria. Ciò è indicato dall'oggetto telemetry che imposta enabled su vero. Il valore della proprietà enabled deve essere true per pubblicare i dati di telemetria per il flag.

La sezione telemetry di un flag di funzionalità ha le proprietà seguenti:

Proprietà Descrizione
enabled Specifica se i dati di telemetria devono essere pubblicati per il flag di funzionalità.
metadata Raccolta di coppie chiave-valore, modellate come dizionario, che possono essere usate per allegare metadati personalizzati relativi al flag di funzionalità agli eventi di valutazione.

Server di pubblicazione di telemetria personalizzati

La gestione personalizzata dei dati di telemetria dei flag di funzionalità è resa possibile implementando un oggetto ITelemetryPublisher e registrandolo nel gestore funzionalità. Ogni volta che viene valutato un flag di funzionalità con dati di telemetria abilitati, l'editore di telemetria registrato può pubblicare l'evento di valutazione corrispondente.

public interface ITelemetryPublisher
{
    ValueTask PublishEvent(EvaluationEvent evaluationEvent, CancellationToken cancellationToken);
}

Il tipo EvaluationEvent è disponibile qui per riferimento.

La registrazione dei server di pubblicazione di telemetria viene eseguita quando si chiama AddFeatureManagement(). Ecco un esempio di configurazione della gestione delle funzionalità per generare dati di telemetria con un'implementazione di ITelemetryPublisher denominata MyTelemetryPublisher.

builder.services
    .AddFeatureManagement()
    .AddTelemetryPublisher<MyTelemetryPublisher>();

Server di pubblicazione Application Insights Telemetry

Il pacchetto Microsoft.FeatureManagement.Telemetry.ApplicationInsights fornisce un'implementazione predefinita dell'editore di telemetria che invia i dati di valutazione dei flag di funzionalità ad Application Insights. Per sfruttare questo vantaggio, aggiungere un riferimento al pacchetto e registrare il server di pubblicazione di telemetria Application Insights, come illustrato di seguito.

builder.services
    .AddFeatureManagement()
    .AddTelemetryPublisher<ApplicationInsightsTelemetryPublisher>();

Nota

Il pacchetto di base Microsoft.FeatureManagement non include questo server di pubblicazione di telemetria.

Un esempio di utilizzo è disponibile nell'esempio EvaluationDataToApplicationInsights.

Prerequisito

Questo server di pubblicazione di telemetria dipende dalla configurazione e dalla registrazione di Application Insights come servizio dell'applicazione. Ad esempio, questa operazione viene eseguita qui nell'applicazione di esempio.

Memorizzazione nella cache

Lo stato della funzionalità viene fornito dal sistema IConfiguration. È previsto che qualsiasi memorizzazione nella cache e aggiornamento dinamico vengano gestito dai provider di configurazione. La gestione delle funzionalità richiede a IConfiguration il valore più recente dello stato di una funzionalità ogni volta che viene verificata l'abilitazione di una funzionalità.

Snapshot

Esistono scenari che richiedono che lo stato di una funzionalità rimanga coerente durante la durata di una richiesta. I valori restituiti dallo standard IFeatureManager possono cambiare se l'origine IConfiguration da cui viene eseguito il pull viene aggiornata durante la richiesta. Questa operazione può essere impedita tramite IFeatureManagerSnapshot. IFeatureManagerSnapshot può essere recuperato nello stesso modo di IFeatureManager. IFeatureManagerSnapshot implementa l'interfaccia di IFeatureManager, ma memorizza nella cache il primo stato valutato di una funzionalità durante una richiesta e restituisce lo stesso stato di funzionalità per tutta la sua durata.

Provider di funzionalità personalizzati

L'implementazione di un provider di funzionalità personalizzato consente agli sviluppatori di eseguire il pull dei flag di funzionalità dalle origini, ad esempio un database o un servizio di gestione delle funzionalità. Il provider di funzionalità incluso usato per impostazione predefinita esegue il pull dei flag di funzionalità dal sistema di configurazione di .NET Core. Ciò consente di definire le funzionalità in un file appsettings.json o in provider di configurazione come Configurazione app di Azure. Questo comportamento può essere sostituito per fornire un controllo completo della posizione da cui le definizioni delle funzionalità vengono lette.

Per personalizzare il caricamento delle definizioni di funzionalità, è necessario implementare l'interfaccia IFeatureDefinitionProvider.

public interface IFeatureDefinitionProvider
{
    Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName);

    IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync();
}

Per usare un'implementazione di IFeatureDefinitionProvider, è necessario aggiungerla alla raccolta di servizi prima di aggiungere la gestione delle funzionalità. Nell'esempio di codice seguente viene aggiunta un'implementazione di IFeatureDefinitionProvider denominata InMemoryFeatureDefinitionProvider.

services.AddSingleton<IFeatureDefinitionProvider, InMemoryFeatureDefinitionProvider>()
        .AddFeatureManagement()

Passaggi successivi

Per informazioni su come usare i flag di funzionalità nelle applicazioni, continuare con le guide introduttive seguenti.

Per informazioni su come usare i filtri di funzionalità, continuare con le esercitazioni seguenti.

Per informazioni su come eseguire esperimenti con flag di funzionalità delle varianti, proseguire con l'esercitazione seguente.