Compartilhar via


Gerenciamento de Recursos do .NET

Microsoft.FeatureManagement
Microsoft.FeatureManagement.AspNetCore

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

A biblioteca de gerenciamento de recursos do .NET fornece uma maneira de desenvolver e expor a funcionalidade do aplicativo com base em sinalizadores de recursos. Depois que um novo recurso é desenvolvido, muitos aplicativos têm requisitos especiais, como quando o recurso deve ser habilitado e sob quais condições. Essa biblioteca fornece uma maneira de definir essas relações e também se integra a padrões comuns de código .NET para tornar possível expor esses recursos.

Os sinalizadores de recursos fornecem uma maneira de os aplicativos .NET e ASP.NET Core ativarem ou desativarem os recursos dinamicamente. Os desenvolvedores podem usar sinalizadores de recursos em casos de uso simples, como instruções condicionais, para cenários mais avançados, como adicionar condicionalmente rotas ou filtros MVC. Os sinalizadores de recursos se baseiam no sistema de configuração do .NET Core. Qualquer provedor de configuração do .NET Core é capaz de atuar como backbone para sinalizadores de recursos.

Aqui estão alguns dos benefícios de usar a biblioteca de gerenciamento de recursos do .NET:

  • Uma convenção comum para o gerenciamento de recursos

  • Barreira inferior à entrada

    • Criado no IConfiguration
    • Dá suporte à configuração do sinalizador de recurso de arquivo JSON
  • Gerenciamento de tempo de vida do Sinalizador de Recursos

    • Os valores de configuração podem ser alterados em tempo real; sinalizadores de recurso podem ser consistentes em toda a solicitação
  • Cenários Simples a Complexos Cobertos

    • Ative/desative recursos por meio do arquivo de configuração declarativa
    • Avalie dinamicamente o estado do recurso com base na chamada ao servidor
  • Extensões de API para a estrutura ASP.NET Core e MVC

    • Roteamento
    • Filtros
    • Atributos de Ação

    A biblioteca de gerenciamento de recursos do .NET é de software livre. Para obter mais informações, acesse o repositório do GitHub.

Sinalizadores de Recursos

Os sinalizadores de recursos são compostos por duas partes, um nome e uma lista de filtros de recursos usados para ativar o recurso.

Filtros de Recurso

Os filtros de recursos definem um cenário para quando um recurso deve ser habilitado. Quando um recurso é avaliado se ele está ativado ou desativado, sua lista de filtros de recursos é percorrida até que um dos filtros decida que o recurso deve ser habilitado. Nesse ponto, o recurso é considerado habilitado e a passagem pelos filtros de recursos é interrompida. Se nenhum filtro de recurso indicar que o recurso deve ser habilitado, ele será considerado desabilitado.

Por exemplo, um filtro de recurso do navegador Microsoft Edge pode ser projetado. Esse filtro de recurso ativaria todos os recursos aos quais ele está anexado, desde que uma solicitação HTTP seja proveniente do Microsoft Edge.

Configuração do sinalizador de recurso

O sistema de configuração do .NET Core é usado para determinar o estado dos sinalizadores de recursos. A base desse sistema é IConfiguration. Qualquer provedor para IConfiguration pode ser usado como o provedor de estado do recurso para a biblioteca de sinalizadores de recurso. Esse sistema permite cenários que vão de appsettings.json à Configuração de Aplicativos do Azure e muito mais.

Declaração do Sinalizador de Recurso

A biblioteca de gerenciamento de recursos dá suporte a appsettings.json como uma fonte de sinalizador de recurso, pois é um provedor para o sistema IConfiguration do .NET Core. Abaixo, temos um exemplo do formato usado para configurar sinalizadores de recursos em um arquivo 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"
                    }
                }
            ]
        }
    }
}

A seção FeatureManagement do documento json é usada por convenção para carregar as configurações do sinalizador de recurso. Na seção acima, vemos três recursos diferentes. Os recursos definem seus filtros de recursos usando a propriedade EnabledFor. Nos filtros de recurso para FeatureT, vemos AlwaysOn. Esse filtro de recurso é interno e, se especificado, sempre habilitará o recurso. O filtro de recurso AlwaysOn não requer nenhuma configuração, portanto, ele tem apenas a propriedade Name. FeatureU não tem filtros em sua propriedade EnabledFor e, portanto, nunca será habilitado. Qualquer funcionalidade que dependa desse recurso que está sendo habilitado não estará acessível, desde que os filtros de recursos permaneçam vazios. No entanto, assim que um filtro de recurso que habilita o recurso é adicionado, ele pode começar a funcionar. FeatureV especifica um filtro de recurso chamado TimeWindow. Esse é um exemplo de um filtro de recurso configurável. Podemos ver no exemplo que o filtro tem uma propriedade Parameters. Isso é usado para configurar o filtro. Nesse caso, os horários de início e término do recurso a ser ativo são configurados.

O esquema detalhado da seção FeatureManagement pode ser encontrado aqui.

Avançado: o uso de dois-pontos ':' é proibido em nomes de sinalizador de recurso.

Declaração de ativação/desativação

O trecho de código a seguir demonstra uma maneira alternativa de definir um recurso que pode ser usado para recursos de ativação/desativação.

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

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

RequirementType

A propriedade RequirementType de um sinalizador de recurso é usada para determinar se os filtros devem usar a lógica Any ou All ao avaliar o estado de um recurso. Se RequirementType não for especificado, o valor padrão será Any.

  • Any significa que apenas um filtro precisa ser avaliado como true para que o recurso seja habilitado.
  • All significa que cada filtro precisa ser avaliado como true para que o recurso seja habilitado.

Um RequirementType de All altera a transversal. Primeiro, se não houver filtros, o recurso será desabilitado. Em seguida, os filtros de recurso são percorridos até que um dos filtros decida que o recurso deve ser desabilitado. Se nenhum filtro indicar que o recurso deve ser desabilitado, ele será considerado habilitado.

"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"
            }
        }
    ]
}

No exemplo acima, FeatureW especifica um RequirementType de All, o que significa que todos os seus filtros devem ser avaliados como verdadeiros para que o recurso seja habilitado. Nesse caso, o recurso está habilitado para 50% dos usuários durante a janela de tempo especificada.

Esquema de Gerenciamento de Recursos da Microsoft

A biblioteca de gerenciamento de recursos também dá suporte ao uso de Microsoft Feature Management schema para declarar sinalizadores de recursos. Esse esquema é independente de linguagem de origem e tem suporte em todas as bibliotecas de gerenciamento de recursos da 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"
                            }
                        }
                    ]
                }
            }
        ]
    }
}

Observação

Se a seção feature_management puder ser encontrada na configuração, a seção FeatureManagement será ignorada.

A biblioteca de gerenciamento de recursos dá suporte a appsettings.json como uma fonte de sinalizador de recurso, pois é um provedor para o sistema IConfiguration do .NET Core. Os sinalizadores de recurso são declarados usando o Microsoft Feature Management schema. Esse esquema é independente de linguagem de origem e tem suporte em todas as bibliotecas de gerenciamento de recursos da Microsoft.

Abaixo, temos um exemplo de declaração de sinalizadores de recurso em um arquivo 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"
                            }
                        }
                    ]
                }
            }
        ]
    }
}

A seção feature_management do documento json é usada por convenção para carregar as configurações do sinalizador de recurso. Os objetos de sinalizador de recurso devem ser listados na matriz feature_flags na seção feature_management. Na seção acima, vemos que fornecemos três recursos diferentes. Um sinalizador de recurso tema as propriedades id e enabled. O id é o nome é usado para identificar e referenciar o sinalizador de recurso. A propriedade enabled especifica o estado habilitado do sinalizador de recurso. Um recurso está DESATIVADO se enabled for false. Se enabled for true, o estado do recurso dependerá do conditions. Se não houver conditions, o recurso está ATIVADO. Se houver conditions e eles forem atendidos, o recurso está ATIVADO. Se houver conditions e eles não forem atendidos, o recurso está DESATIVADO. A propriedade conditions declara as condições usadas para habilitar dinamicamente o recurso. Os recursos definem seus filtros de recursos na matriz client_filters. FeatureV especifica um filtro de recurso chamado Microsoft.TimeWindow. Esse é um exemplo de um filtro de recurso configurável. Podemos ver no exemplo que o filtro tem uma propriedade Parameters. Isso é usado para configurar o filtro. Nesse caso, os horários de início e término do recurso a ser ativo são configurados.

Avançado: o uso de dois-pontos ':' é proibido em nomes de sinalizador de recurso.

RequirementType

A propriedade requirement_type de conditions é usada para determinar se os filtros devem usar a lógicaAny ou All ao avaliar o estado de um recurso. Se requirement_type não for especificado, o valor padrão será Any.

  • Any significa que apenas um filtro precisa ser avaliado como true para que o recurso seja habilitado.
  • All significa que cada filtro precisa ser avaliado como true para que o recurso seja habilitado.

Um requirement_type de All altera a transversal. Primeiro, se não houver filtro, o recurso será desabilitado. Se houver filtros, os filtros de recurso serão percorridos até que um dos filtros decida que o recurso deve ser desabilitado. Se nenhum filtro indicar que o recurso deve ser desabilitado, ele será considerado habilitado.

{
    "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"
                }
            }
        ]
    }
}

No exemplo acima, FeatureW especifica um requirement_type de All, o que significa que todos os seus filtros devem ser avaliados como verdadeiros para que o recurso seja habilitado. Nesse caso, o recurso será habilitado para 50% dos usuários durante a janela de tempo especificada.

Esquema de Gerenciamento de Recursos do .NET

Nas versões anteriores, o esquema primário da biblioteca de gerenciamento de recursos era o .NET feature management schema. A partir da v4.0.0, novos recursos, incluindo variantes e telemetria, não serão compatíveis com o esquema de gerenciamento de recursos do .NET.

Observação

Se um sinalizador de recurso escrito com Microsoft Feature Management schema pode ser encontrado na configuração, qualquer sinalizador de recurso escrito com .NET feature management schema será ignorado.

Consumo

A forma básica de gerenciamento de recursos é verificar se um sinalizador de recurso está habilitado e executar ações com base no resultado. Isso é feito por meio do método IFeatureManager do IsEnabledAsync.

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

Registro do Serviço

O gerenciador de recursos se baseia na injeção de dependência do .NET Core. Você pode registrar os serviços de gerenciamento de recursos usando as convenções padrão.

using Microsoft.FeatureManagement;

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

Por padrão, o gerenciador de recursos recupera a configuração do sinalizador de recursos da seção "FeatureManagement" dos dados de configuração do .NET Core. Se a seção "FeatureManagement" não existir, a configuração será considerada vazia.

Observação

Você também pode especificar que a configuração do sinalizador de recurso deve ser recuperada de uma seção de configuração diferente passando a seção para AddFeatureManagement. O seguinte exemplo instrui o gerenciador de recursos a fazer a leitura de outra seção chamada "MyFeatureFlags":

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

Injeção de dependência

Ao usar a biblioteca de gerenciamento de recursos com MVC, o IFeatureManager pode ser obtida por meio da injeção de dependência.

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

Serviços de Gerenciamento de Recursos com Escopo

O método AddFeatureManagement adiciona serviços de gerenciamento de recursos como singletons dentro do aplicativo, mas há cenários em que pode ser necessário que os serviços de gerenciamento de recursos sejam adicionados como serviços com escopo. Por exemplo, os usuários podem querer usar filtros de recurso que consomem serviços com escopo para informações de contexto. Nesse caso, o método AddScopedFeatureManagement deve ser usado. Isso garante que os serviços de gerenciamento de recursos, incluindo filtros de recursos, sejam adicionados como serviços com escopo.

services.AddScopedFeatureManagement();

Integração do ASP.NET Core

A biblioteca de gerenciamento de recursos fornece funcionalidade no ASP.NET Core e MVC para habilitar cenários comuns de sinalizador de recursos em aplicativos Web. Esses recursos estão disponíveis referenciando o pacote NuGet Microsoft.FeatureManagement.AspNetCore.

Controladores e Ações

O controlador e as ações do MVC podem exigir que um determinado recurso ou uma de qualquer lista de recursos seja habilitado para ser executado. Isso pode ser feito usando um FeatureGateAttribute, que pode ser encontrado no namespace Microsoft.FeatureManagement.Mvc.

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

O acima HomeController é fechado por "FeatureX". "FeatureX" deve ser habilitado antes de qualquer ação que o HomeController contenha possa ser executada.

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

A ação MVC Index acima requer que "FeatureX" seja habilitado antes que ele possa ser executado.

Tratamento de Ação Desabilitado

Quando um controlador ou ação MVC é bloqueado porque nenhum dos recursos especificados está habilitado, um IDisabledFeaturesHandler registrado será invocado. Por padrão, um manipulador minimalista é registrado, o que retorna HTTP 404. Isso pode ser substituído usando o IFeatureManagementBuilder ao registrar sinalizadores de recurso.

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

Exibir

Em exibições de MVC, as marcas <feature> podem ser usadas para renderizar condicionalmente o conteúdo com base em se um recurso está habilitado ou não.

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

Você também pode negar a avaliação auxiliar de marca para exibir o conteúdo quando um recurso ou conjunto de recursos estiver desabilitado. Ao definir negate="true" no exemplo abaixo, o conteúdo só será renderizado se FeatureX estiver desabilitado.

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

A marca <feature> pode referenciar vários recursos especificando uma lista separada por vírgulas de recursos no atributo name.

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

Por padrão, todos os recursos listados devem ser habilitados para que a marca de recurso seja renderizada. Esse comportamento pode ser substituído adicionando o atributo requirement, conforme visto no exemplo abaixo.

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

Em exibições de MVC, as marcas <feature> podem ser usadas para renderizar condicionalmente o conteúdo com base em se um recurso está habilitado ou se uma variante específica de um recurso é atribuída. Para obter mais informações, consulte a seção variantes.

<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>

Você também pode negar a avaliação auxiliar de marca para exibir o conteúdo quando um recurso ou conjunto de recursos estiver desabilitado. Ao definir negate="true" no exemplo abaixo, o conteúdo só será renderizado se FeatureX estiver desabilitado.

<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>

A marca <feature> pode referenciar vários recursos/variantes especificando uma lista separada por vírgulas de recursos/variantes no atributo 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>

Observação

Se variant for especificado, apenas um recurso deverá ser especificado.

Por padrão, todos os recursos listados devem ser habilitados para que a marca de recurso seja renderizada. Esse comportamento pode ser substituído adicionando o atributo requirement, conforme visto no exemplo abaixo.

Observação

Se um requirement de And for usado em conjunto com variant, um erro será gerado, pois várias variantes nunca poderão ser atribuídas.

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

A marca <feature> requer que um auxiliar de marca funcione. Isso pode ser feito adicionando o auxiliar de marca de gerenciamento de recursos ao arquivo ViewImports.cshtml.

@addTagHelper *, Microsoft.FeatureManagement.AspNetCore

Filtros do MVC

Os filtros de ação do MVC podem ser configurados para serem executados condicionalmente com base no estado de um recurso. Isso é feito registrando filtros do MVC de maneira consciente do recurso. O pipeline de gerenciamento de recursos dá suporte a filtros de Ação do MVC assíncronos, que implementam IAsyncActionFilter.

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

O código acima adiciona um filtro do MVC chamado SomeMvcFilter. Esse filtro só é disparado no pipeline do MVC se "FeatureX" estiver habilitado.

Páginas do Razor

As páginas Razor do MVC podem exigir que um determinado recurso ou uma de qualquer lista de recursos seja habilitado para ser executado. Isso pode ser feito usando um FeatureGateAttribute, que pode ser encontrado no namespace Microsoft.FeatureManagement.Mvc.

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

O código acima configura uma página Razor para exigir que o "FeatureX" seja habilitado. Se o recurso não estiver habilitado, a página gerará um resultado HTTP 404 (NotFound).

Quando usado em páginas Razor, o FeatureGateAttribute deve ser colocado no tipo de manipulador de página. Ele não pode ser colocado em métodos de manipulador individuais.

Criação de aplicativo

A biblioteca de gerenciamento de recursos pode ser usada para adicionar branches de aplicativo e middleware que são executados condicionalmente com base no estado do recurso.

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

Com a chamada acima, o aplicativo adiciona um componente de middleware que só aparece no pipeline de solicitação se o recurso "FeatureX" estiver habilitado. Se o recurso estiver habilitado/desabilitado durante o runtime, o pipeline de middleware poderá ser alterado dinamicamente.

Isso cria a funcionalidade mais genérica para gerar um branch de todo o aplicativo com base em um sinalizador de recurso.

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

Como implementar um filtro de recursos

A criação de um filtro de recursos fornece uma maneira de habilitar recursos com base nos critérios definidos por você. Para implementar um filtro de recurso, a interface IFeatureFilter deve ser implementada. IFeatureFilter tem um único método chamado EvaluateAsync. Quando um recurso especifica que ele pode ser habilitado para um filtro de recurso, o método EvaluateAsync é chamado. Se EvaluateAsync retornar true, significa que o recurso deve ser habilitado.

O trecho de código a seguir demonstra como adicionar um filtro de recurso MyCriteriaFilter personalizado.

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

Os filtros de recurso são registrados chamando AddFeatureFilter<T> no IFeatureManagementBuilder retornado de AddFeatureManagement. Esses filtros de recursos têm acesso aos serviços que existem dentro da coleção de serviços que foi usada para adicionar sinalizadores de recursos. A injeção de dependência pode ser usada para recuperar esses serviços.

Observação

Quando os filtros são referenciados nas configurações do sinalizador de recurso (por exemplo, appsettings.json), a parte Filtro do nome do tipo deve ser omitida. Para obter mais informações, consulte a seção Filter Alias Attribute.

Filtros de recursos parametrizados

Alguns filtros de recursos exigem parâmetros para decidir se um recurso deve ser ativado ou não. Por exemplo, um filtro de recurso do navegador pode ativar um recurso para um determinado conjunto de navegadores. Pode ser desejado que os navegadores Edge e Chrome habilitem um recurso, enquanto o Firefox não. Para fazer isso, um filtro de recurso pode ser projetado para esperar parâmetros. Esses parâmetros seriam especificados na configuração do recurso e, no código, seriam acessíveis por meio do parâmetro FeatureFilterEvaluationContext de 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 tem uma propriedade chamada Parameters. Esses parâmetros representam uma configuração bruta que o filtro de recursos pode usar para decidir como avaliar se o recurso deve ser habilitado ou não. Para usar o filtro de recursos do navegador como exemplo mais uma vez, o filtro pode usar Parameters para extrair um conjunto de navegadores permitidos que seriam especificados para o recurso e, em seguida, verificar se a solicitação está sendo enviada de um desses navegadores.

[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
    }
}

Filtrar Atributo de Alias

Quando um filtro de recurso é registrado para um sinalizador de recurso, o alias usado na configuração é o nome do tipo de filtro de recurso com o sufixo Filtro, se houver, removido. Por exemplo, MyCriteriaFilter seria chamado de MyCriteria na configuração.

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

Isso pode ser substituído usando o FilterAliasAttribute. Um filtro de recurso pode ser decorado com esse atributo para declarar o nome que deve ser usado na configuração para referenciar esse filtro de recurso dentro de um sinalizador de recurso.

Filtros de Recursos Ausentes

Se um recurso estiver configurado para ser habilitado para um filtro de recurso específico e esse filtro de recurso não for registrado, uma exceção será gerada quando o recurso for avaliado. A exceção pode ser desabilitada usando as opções de gerenciamento de recursos.

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

Como usar HttpContext

Os filtros de recursos podem avaliar se um recurso deve ser habilitado com base nas propriedades de uma solicitação HTTP. Isso é executado inspecionando o contexto HTTP. Um filtro de recurso pode obter uma referência ao contexto HTTP obtendo uma IHttpContextAccessorpor meio de dependência.

public class BrowserFilter : IFeatureFilter
{
    private readonly IHttpContextAccessor _httpContextAccessor;

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

OIHttpContextAccessor deve ser adicionado ao contêiner de injeção de dependência na inicialização para que ele esteja disponível. Ele pode ser registrado no IServiceCollection usando o método a seguir.

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

Avançado: IHttpContextAccessor/HttpContext não deve ser usado nos componentes Razor de aplicativos Blazor do lado do servidor. A abordagem recomendada para passar o contexto http em aplicativos Blazor é copiar os dados para um serviço com escopo. Para aplicativos Blazor, AddScopedFeatureManagement deve ser usado para registrar os serviços de gerenciamento de recursos. Para obter mais informações, consulte a seção Scoped Feature Management Services.

Fornecer um Contexto para Avaliação de Recursos

Em aplicativos de console, não há nenhum contexto ambiente como HttpContext que os filtros de recursos podem adquirir e utilizar para verificar se um recurso deve estar ativado ou desativado. Nesse caso, os aplicativos precisam fornecer um objeto que represente um contexto no sistema de gerenciamento de recursos para uso por filtros de recursos. Isso é feito usando IFeatureManager.IsEnabledAsync<TContext>(string featureName, TContext appContext). O objeto appContext fornecido ao gerenciador de recursos pode ser usado por filtros de recurso para avaliar o estado de um recurso.

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

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

Filtros de Recursos Contextuais

Os filtros de recursos contextuais implementam a interface IContextualFeatureFilter<TContext>. Esses filtros de recursos especiais podem aproveitar o contexto passado quando IFeatureManager.IsEnabledAsync<TContext> é chamado. O parâmetro de tipo TContext no IContextualFeatureFilter<TContext> descreve que tipo de contexto o filtro é capaz de manipular. Isso permite que o desenvolvedor de um filtro de recursos contextual descreva o que é necessário para aqueles que desejam utilizá-lo. Como cada tipo é descendente de objeto, um filtro que implementa IContextualFeatureFilter<object> pode ser chamado para qualquer contexto fornecido. Para ilustrar um exemplo de um filtro de recurso contextual mais específico, considere um recurso habilitado se uma conta estiver em uma lista configurada de contas habilitadas.

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
    }
}

Podemos ver que é o AccountIdFilter exige que um objeto que implementa IAccountContext seja fornecido para ser capaz de avaliar o estado de um recurso. Ao usar esse filtro de recurso, o chamador precisa garantir que o objeto passado seja implementado IAccountContext.

Observação

Somente uma única interface de filtro de recurso pode ser implementada por um único tipo. Tentar adicionar um filtro de recurso que implementa mais de uma única interface de filtro de recurso resulta em um ArgumentException.

Como Usar Filtros Contextuais e Não Contextuais Com o Mesmo Alias

Filtros de IFeatureFilter e IContextualFeatureFilter podem compartilhar o mesmo alias. Especificamente, você pode ter um alias de filtro compartilhado por 0 ou 1 IFeatureFilter e 0 ou N IContextualFeatureFilter<ContextType>, desde que haja no máximo um filtro aplicável para ContextType.

A passagem a seguir descreve o processo de seleção de um filtro quando filtros contextuais e não contextuais de mesmo nome são registrados em um aplicativo.

Digamos que você tenha um filtro não contextual chamado FilterA e dois filtros contextuais FilterB e FilterC que aceitam os contextos TypeB e TypeC, respectivamente. Todos os três filtros compartilham o mesmo alias SharedFilterName.

Você também tem um sinalizador de recurso MyFeature que usa o filtro de recurso SharedFilterName em sua configuração.

Se todos os três filtros forem registrados:

  • Quando você chama IsEnabledAsync("MyFeature"), o FilterA é usado para avaliar o sinalizador de recurso.
  • Quando você chama IsEnabledAsync("MyFeature", contexto), se o tipo de contexto for TypeB, FilterB é usado. Se o tipo de contexto for TypeC, FilterC será usado.
  • Quando você chama IsEnabledAsync("MyFeature", contexto), se o tipo de contexto for TypeF, FilterA é usado.

Filtros de Recurso Internos

Há alguns filtros de recursos que vêm com o pacote Microsoft.FeatureManagement: PercentageFilter, TimeWindowFilter, ContextualTargetingFilter e TargetingFilter. Todos os filtros, exceto o TargetingFilter, são adicionados automaticamente quando o gerenciamento de recursos é registrado pelo método AddFeatureManagement. OTargetingFilter é adicionado com o método WithTargeting detalhado na seção Targeting abaixo.

Cada um dos filtros de recursos internos tem seus próprios parâmetros. Aqui está a lista de filtros de recursos junto com exemplos.

Microsoft.Percentage

Esse filtro fornece a capacidade de habilitar um recurso com base em uma porcentagem definida.

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

Microsoft.TimeWindow

Esse filtro fornece a capacidade de habilitar um recurso com base em uma janela de tempo. Se apenas End for especificado, o recurso será considerado até esse momento. Se apenas Start for especificado, o recurso será considerado em todos os pontos após esse tempo.

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

A janela de tempo pode ser configurada para se repetir periodicamente. Isso pode ser útil para os cenários em que talvez seja necessário ativar um recurso durante um período de tráfego baixo ou alto de um dia ou determinados dias de uma semana. Para expandir a janela de tempo individual para janelas de tempo recorrentes, a regra de recorrência deve ser especificada no parâmetro Recurrence.

Observação

Start e End devem ser especificados para habilitar 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"
                    }
                }
            }
        }
    ]
}

As configurações Recurrence são compostas por duas partes: Pattern (com que frequência a janela de tempo se repete) e Range (por quanto tempo o padrão de recorrência se repete).

Padrão de Recorrência

Há dois tipos de padrão de recorrência possíveis: Daily e Weekly. Por exemplo, uma janela de tempo pode repetir "todos os dias", "a cada três dias", "todas as segundas- feiras" ou "todas as sextas-feiras".

Dependendo do tipo, determinados campos do Pattern são necessários, opcionais ou ignorados.

  • Daily

    O padrão de recorrência diária faz com que a janela de tempo se repita com base em um número de dias entre cada ocorrência.

    Propriedade Relevância Descrição
    Tipo Obrigatório Deve ser definido como Daily.
    Intervalo Opcional Especifica o número de dias entre cada ocorrência. O valor padrão é 1.
  • Weekly

    O padrão de recorrência semanal faz com que a janela de tempo se repita no mesmo dia ou dias da semana, com base no número de semanas entre cada conjunto de ocorrências.

    Propriedade Relevância Descrição
    Tipo Obrigatório Deve ser definido como Weekly.
    DaysOfWeek Obrigatório Especifica em quais dias da semana o evento ocorre.
    Intervalo Opcional Especifica o número de semanas entre cada conjunto de ocorrências. O valor padrão é 1.
    FirstDayOfWeek Opcional Especifica qual dia é considerado o primeiro dia da semana. O valor padrão é Sunday.

    O exemplo a seguir repete a janela de tempo a cada duas segundas e terças-feiras

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

Observação

Start deve ser uma primeira ocorrência válida que se ajuste ao padrão de recorrência. Além disso, a duração da janela de tempo não pode ser maior do que a frequência com que ela ocorre. Por exemplo, é inválido ter uma janela de tempo de 25 horas recorrente todos os dias.

Intervalo de recorrência

Há três tipos de intervalo de recorrência possíveis: NoEnd, EndDate e Numbered.

  • NoEnd

    O intervalo NoEnd faz com que a recorrência ocorra indefinidamente.

    Propriedade Relevância Descrição
    Tipo Obrigatório Deve ser definido como NoEnd.
  • EndDate

    O intervalo EndDate faz com que a janela de tempo ocorra em todos os dias que se ajustem ao padrão aplicável até a data de término.

    Propriedade Relevância Descrição
    Tipo Obrigatório Deve ser definido como EndDate.
    EndDate Obrigatório Especifica a data e hora para parar de aplicar o padrão. Desde que a hora de início da última ocorrência caia antes da data de término, a hora de término dessa ocorrência tem permissão para se estender além dela.

    O exemplo a seguir repetirá a janela de tempo todos os dias até que a última ocorrência ocorra em 1º de abril de 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

    O intervalo Numbered faz com que a janela de tempo ocorra um número fixo de vezes (com base no padrão).

    Propriedade Relevância Descrição
    Tipo Obrigatório Deve ser definido como Numbered.
    NumberOfOccurrences Obrigatório Especifica o número de ocorrências.

    O exemplo a seguir repetirá a janela de tempo na segunda e na terça-feira até que haja três ocorrências, que ocorrem respectivamente em 1º de abril (seg), 2 de abril (ter) e 8 de abril (segunda-feira).

    "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
        }
    }
    

Para criar uma regra de recorrência, você deve especificar Pattern e Range. Qualquer tipo de padrão pode funcionar com qualquer tipo de intervalo.

Avançado: o deslocamento de fuso horário da propriedade Start é aplicado às configurações de recorrência.

Microsoft.Targeting

Esse filtro fornece a capacidade de habilitar um recurso para um público-alvo. Uma explicação detalhada do direcionamento é explicada na seção direcionamento abaixo. Os parâmetros de filtro incluem um objeto Audience que descreve usuários, grupos, usuários/grupos excluídos e um percentual padrão da base de usuários que deve ter acesso ao recurso. Cada objeto de grupo listado na seção Groups também deve especificar qual porcentagem dos membros do grupo deve ter acesso. Se um usuário for especificado na seção Exclusion, diretamente ou se o usuário estiver em um grupo excluído, o recurso será desabilitado. Caso contrário, se um usuário for especificado diretamente na seção Users ou se o usuário estiver no percentual incluído de qualquer uma das distribuições de grupo ou se o usuário se enquadrar no percentual de distribuição padrão, esse usuário terá o recurso habilitado.

"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"
                        ]
                    }
                }
            }
        }
    ]
}

Namespaces do Alias do Filtro de Recurso

Todos os alias de filtro de recurso internos estão no namespace Microsoft do filtro de recurso. Isso é para evitar conflitos com outros filtros de recursos que podem compartilhar o mesmo alias. Os segmentos de um namespace de filtro de recurso são divididos pelo caractere '.'. Um filtro de recurso pode ser referenciado por seu alias totalmente qualificado, como Microsoft.Percentage ou pelo último segmento que, no caso de Microsoft.Percentage, é Percentage.

Direcionamento

A segmentação é uma estratégia de gerenciamento de recursos que permite aos desenvolvedores implementar progressivamente novos recursos para sua base de usuários. A estratégia baseia-se no conceito de atingir um conjunto de usuários conhecido como público-alvo. Um público é composto por usuários, grupos específicos e uma porcentagem designada de toda a base de usuários. Os grupos incluídos na audiência podem ser divididos ainda mais em percentuais de seus membros totais.

As etapas a seguir demonstram um exemplo de uma distribuição progressiva para um novo recurso "Beta":

  1. Os usuários individuais Jonas e Alice recebem acesso ao Beta
  2. Outro usuário, Marcos, solicita a aceitação e está incluído.
  3. Vinte por cento de um grupo conhecido como usuários "Ring1" estão incluídos no Beta.
  4. O número de usuários "Ring1" incluídos no beta é aumentado para 100%.
  5. Cinco por cento da base de usuários está incluída na versão beta.
  6. O percentual de distribuição é aumentado em até 100% e o recurso é completamente distribuído.

Essa estratégia para implantar um recurso é interna na biblioteca por meio do filtro de recursos Microsoft.Targeting incluído.

Direcionamento em um aplicativo Web

Um aplicativo Web de exemplo que usa o filtro de recurso de direcionamento está disponível no projeto de exemplo FeatureFlagDemo.

Para começar a usar o aplicativo TargetingFilter, ele deve ser adicionado à coleção de serviços do aplicativo, assim como qualquer outro filtro de recurso. Ao contrário de outros filtros internos, o TargetingFilter depende de outro serviço a ser adicionado à coleção de serviços do aplicativo. Esse serviço é um ITargetingContextAccessor.

O tipo de implementação usado para o serviço ITargetingContextAccessor deve ser implementado pelo aplicativo que está usando o filtro de direcionamento. Aqui está um exemplo de como configurar o gerenciamento de recursos em um aplicativo Web para usar o TargetingFilter com uma implementação de ITargetingContextAccessor chamada HttpContextTargetingContextAccessor.

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

O acessador de contexto de direcionamento e TargetingFilter são registrados chamando WithTargeting<T> no IFeatureManagementBuilder.

ITargetingContextAccessor

Para usar o TargetingFilter no aplicativo Web, é necessária uma implementação de ITargetingContextAccessor. Isso ocorre porque, quando uma avaliação de direcionamento está sendo executada, informações como qual usuário está sendo avaliado no momento são necessárias. Essas informações são conhecidas como o contexto de direcionamento. Aplicativos Web diferentes podem extrair essas informações de locais diferentes. Alguns exemplos comuns de onde um aplicativo pode efetuar pull do contexto de direcionamento são o contexto HTTP da solicitação ou um banco de dados.

Um exemplo que extrai informações de contexto de direcionamento do contexto HTTP do aplicativo é incluído no projeto de exemplo FeatureFlagDemo. Esse método depende do uso de IHttpContextAccessor, que é discutido aqui.

Direcionamento em um Aplicativo de Console

O filtro de direcionamento depende de um contexto de direcionamento para avaliar se um recurso deve ser ativado. Esse contexto de direcionamento contém informações como em qual usuário está sendo avaliado no momento e em quais grupos o usuário está. Em aplicativos de console, normalmente não há nenhum contexto ambiente disponível para fluir essas informações para o filtro de direcionamento, portanto, elas devem ser passadas diretamente quando FeatureManager.IsEnabledAsync são chamadas. Isso tem suporte usando o ContextualTargetingFilter. Os aplicativos que precisam flutuar o contexto de direcionamento para o gerenciador de recursos devem usá-lo em vez do TargetingFilter.

Como ContextualTargetingFilter é umIContextualTargetingFilter<ITargetingContext>, uma implementação de ITargetingContext deve ser passada para IFeatureManager.IsEnabledAsync para que ele seja capaz de avaliar e ativar um recurso.

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

await fm.IsEnabledAsync(featureName, targetingContext);

O ContextualTargetingFilter ainda usa o alias de filtro de recurso Microsoft.Targeting, portanto, a configuração desse filtro é consistente com o que é mencionado nessa seção.

Um exemplo que usa o aplicativo ContextualTargetingFilter em um console está disponível no projeto de exemplo TargetingConsoleApp.

Opções de Avaliação de Direcionamento

As opções estão disponíveis para personalizar como a avaliação de direcionamento é executada em todos os recursos. Essas opções podem ser configuradas ao configurar o gerenciamento de recursos.

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

Exclusão de Direcionamento

Ao definir um Público-Alvo, os usuários e grupos podem ser excluídos do público-alvo. Isso é útil quando um recurso está sendo distribuído para um grupo de usuários, mas alguns usuários ou grupos precisam ser excluídos da distribuição. A exclusão é definida adicionando uma lista de usuários e grupos à propriedade Exclusion do público-alvo.

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

No exemplo acima, o recurso está habilitado para usuários chamados Jeff e Alicia. Ele também está habilitado para usuários no grupo chamado Ring0. No entanto, se o usuário for chamado Mark, o recurso será desabilitado, independentemente de estar no grupo Ring0 ou não. As exclusões têm prioridade sobre o restante do filtro de direcionamento.

Variantes

Quando novos recursos são adicionados a um aplicativo, pode chegar um momento em que um recurso tem várias opções de design propostas diferentes. Uma solução comum para decidir sobre um design é uma forma de teste A/B, que envolve fornecer uma versão diferente do recurso para diferentes segmentos da base de usuários e escolher uma versão com base na interação do usuário. Nesta biblioteca, essa funcionalidade é habilitada por meio da representação de diferentes configurações de um recurso com variantes.

As variantes permitem que um sinalizador de recurso se torne mais do que um sinalizador simples de ativação/desativação. Uma variante representa um valor de um sinalizador de recurso que pode ser uma cadeia de caracteres, um número, um booliano ou até mesmo um objeto de configuração. Um sinalizador de recurso que declara variantes deve definir em quais circunstâncias cada variante deve ser usada, o que é abordado com mais detalhes na seção Alocando Variantes.

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

Como Obtendo Variantes

Para cada recurso, uma variante pode ser recuperada usando o método IVariantFeatureManager do 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

Depois que uma variante é recuperada, a configuração de uma variante pode ser usada diretamente como um IConfigurationSection da propriedade Configuration da variante. Outra opção é associar a configuração a um objeto usando. Padrão de associação de configuração do NET.

IConfigurationSection variantConfiguration = variant.Configuration;

MyFeatureSettings settings = new MyFeatureSettings();

variantConfiguration.Bind(settings);

A variante retornada depende do usuário que está sendo avaliado no momento e essas informações são obtidas de uma instância de TargetingContext. Esse contexto pode ser passado ao chamar GetVariantAsync ou pode ser recuperado automaticamente de uma implementação de ITargetingContextAccessor se um estiver registrado.

Declaração do Sinalizador de Recurso Variante

Em comparação com os sinalizadores de recursos normais, os sinalizadores de recursos variantes têm duas propriedades adicionais: variants e allocation. A propriedade variants é uma matriz que contém as variantes definidas para esse recurso. A propriedade allocation define como essas variantes devem ser alocadas para o recurso. Assim como declarar sinalizadores de recursos normais, você pode configurar sinalizadores de recursos variantes em um arquivo json. Aqui está um exemplo de um sinalizador de recurso variante.

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

Definindo Variantes

Cada variante tem duas propriedades: um nome e uma configuração. O nome é usado para se referir a uma variante específica e a configuração é o valor dessa variante. A configuração pode ser definida usando as propriedades configuration_reference ou configuration_value. configuration_reference é um caminho de cadeia de caracteres que faz referência a uma seção da configuração atual que contém a declaração do sinalizador de recurso. configuration_value é uma configuração embutida que pode ser uma cadeia de caracteres, número, booliano ou objeto de configuração. Se ambos forem especificados, configuration_value será usado. Se nenhum dos dois for especificado, a propriedade Configuration da variante retornada será nula.

Uma lista de todas as variantes possíveis é definida para cada recurso na propriedade 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"
        }
    }
}

Alocando Variantes

O processo de alocação das variantes de um recurso é determinado pela propriedade allocation do recurso.

"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"
    } 
]

A configuração allocation de um recurso tem as seguintes propriedades:

Propriedade Descrição
default_when_disabled Especifica qual variante deve ser usada quando uma variante é solicitada enquanto o recurso é considerado desabilitado.
default_when_enabled Especifica qual variante deve ser usada quando uma variante é solicitada enquanto o recurso é considerado habilitado e nenhuma outra variante foi atribuída ao usuário.
user Especifica uma variante e uma lista de usuários aos quais essa variante deve ser atribuída.
group Especifica uma variante e uma lista de grupos. A variante será atribuída se o usuário estiver em pelo menos um dos grupos.
percentile Especifica uma variante e um intervalo de porcentagem em que o percentual calculado do usuário precisa se ajustar para que essa variante seja atribuída.
seed O valor no qual os cálculos de porcentagem para percentile se baseiam. O cálculo de porcentagem para um usuário específico será o mesmo em todos os recursos se o mesmo valor seed for usado. Se nenhum seed for especificado, uma semente padrão será criada com base no nome do recurso.

No exemplo acima, se o recurso não estiver habilitado, o gerenciador de recursos atribuirá a variante marcada como default_when_disabled ao usuário atual, que é Small nesse caso.

Se o recurso estiver habilitado, o gerenciador de recursos verificará as alocações user, group e percentile nessa ordem para atribuir uma variante. Para este exemplo específico, se o usuário que está sendo avaliado for nomeado Marsha, no grupo nomeado Ring1, ou se o usuário estiver entre o percentil 0 e 10, a variante especificada será atribuída ao usuário. Nesse caso, todos eles retornariam a variante Big. Se nenhuma dessas alocações corresponder, o usuário recebe a variante default_when_enabled, que é Small.

A lógica de alocação é semelhante ao filtro de recursos Microsoft.Targeting, mas há alguns parâmetros presentes no direcionamento que não estão em alocação e vice-versa. Os resultados de direcionamento e alocação não estão relacionados.

Observação

Para permitir a alocação de variantes de recursos, você precisa se registrar ITargetingContextAccessor. Isso pode ser feito chamando o método WithTargeting<T>.

Substituindo o estado habilitado com uma variante

Você pode usar variantes para substituir o estado habilitado de um sinalizador de recurso. Isso oferece às variantes a oportunidade de estender a avaliação de um sinalizador de recurso. Se um chamador estiver verificando se um sinalizador com variantes está habilitado, o gerenciador de recursos verificará se a variante atribuída ao usuário atual está configurada para substituir o resultado. Isso é feito usando a propriedade variante status_overrideopcional. Por padrão, essa propriedade é definida como None, o que significa que a variante não afeta se o sinalizador é considerado habilitado ou desabilitado. A configuração status_override para Enabled permite que a variante, quando escolhida, substitua um sinalizador a ser habilitado. A configuração status_override para Disabled fornece a funcionalidade oposta, desabilitando o sinalizador quando a variante for escolhida. Um recurso com um estado enabled de false não pode ser substituído.

Se você estiver usando um sinalizador de recurso com variantes binárias, a propriedade status_override poderá ser muito útil. Ele permite que você continue usando APIs como IsEnabledAsync e FeatureGateAttribute em seu aplicativo, ao mesmo tempo em que se beneficia dos novos recursos que vêm com variantes, como alocação de percentil e semente.

{
    "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"
        }
    ]
}

No exemplo acima, o recurso está sempre habilitado. Se o usuário atual estiver no intervalo de percentil calculado de 10 a 20, a variante On será retornada. Caso contrário, a variante Off será retornada e, como status_override é igual a Disabled, o recurso agora será considerado desabilitado.

Variantes na Injeção de Dependência

Os sinalizadores de recursos variantes podem ser usados em conjunto com a injeção de dependência para exibir diferentes implementações de um serviço para usuários diferentes. Isso é feito usando a interface IVariantServiceProvider<TService>.

IVariantServiceProvider<IAlgorithm> algorithmServiceProvider;
...

IAlgorithm forecastAlgorithm = await algorithmServiceProvider.GetServiceAsync(cancellationToken); 

No trecho de código acima, o IVariantServiceProvider<IAlgorithm> recupera uma implementação de IAlgorithm do contêiner de injeção de dependência. A implementação escolhida depende de:

  • O sinalizador de recurso com o qual o serviço IAlgorithm foi registrado.
  • A variante alocada para esse recurso.

O IVariantServiceProvider<T> ser disponibilizado para o aplicativo chamando IFeatureManagementBuilder.WithVariantService<T>(string featureName). Veja um exemplo abaixo.

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

A chamada acima disponibiliza IVariantServiceProvider<IAlgorithm> na coleção de serviços. As implementações de IAlgorithm devem ser adicionadas separadamente por meio de um método de adição, como services.AddSingleton<IAlgorithm, SomeImplementation>(). A implementação do IAlgorithm que o IVariantServiceProvider usa depende do sinalizador de recurso variante ForecastAlgorithm. Se nenhuma implementação de IAlgorithm for adicionada à coleção de serviços, o IVariantServiceProvider<IAlgorithm>.GetServiceAsync() retornará uma tarefa com um resultado nulo.

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

Atributo de Alias de Serviço Variante

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

O provedor de serviços variante usará os nomes de tipo de implementações para corresponder à variante alocada. Se um serviço variante for decorado com o VariantServiceAliasAttribute, o nome declarado nesse atributo deverá ser usado na configuração para fazer referência a esse serviço variante.

Telemetria

Quando uma alteração de sinalizador de recurso é implantada, geralmente é importante analisar seu efeito em um aplicativo. Por exemplo, aqui estão algumas perguntas que podem surgir:

  • Meus sinalizadores estão habilitados/desabilitados conforme o esperado?
  • Os usuários direcionados estão recebendo acesso a um determinado recurso conforme o esperado?
  • Qual variante um usuário específico vê?

Esses tipos de perguntas podem ser respondidas por meio da emissão e análise de eventos de avaliação do sinalizador de recurso. Essa biblioteca dá suporte à emissão desses eventos por meio de editores de telemetria. Um ou muitos editores de telemetria podem ser registrados para publicar eventos sempre que os sinalizadores de recursos são avaliados.

Habilitando a telemetria

Por padrão, os sinalizadores de recursos não têm telemetria emitida. Para publicar a telemetria de um determinado sinalizador de recurso, o sinalizador DEVE declarar que ele está habilitado para emissão de telemetria.

Para sinalizadores de recursos definidos no appsettings.json, isso é feito usando a propriedade telemetry.

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

O trecho de código de appsettings acima define um sinalizador de recurso chamado MyFeatureFlag habilitado para telemetria. Isso é indicado pelo objeto telemetry que define enabled como true. O valor da propriedade enabled deve ser true para publicar telemetria para o sinalizador.

A seção telemetry de um sinalizador de recurso tem as seguintes propriedades:

Propriedade Descrição
enabled Especifica se a telemetria deve ser publicada para o sinalizador de recurso.
metadata Uma coleção de pares chave-valor, modelada como um dicionário, que pode ser usada para anexar metadados personalizados sobre o sinalizador de recurso a eventos de avaliação.

Editores de Telemetria Personalizados

A manipulação personalizada da telemetria do sinalizador de recurso é possível implementando um ITelemetryPublisher e registrando-o no gerenciador de recursos. Sempre que um sinalizador de recurso que tem a telemetria habilitada é avaliado, o publicador de telemetria registrado tem a chance de publicar o evento de avaliação correspondente.

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

O tipo EvaluationEvent pode ser encontrado aqui para referência.

O registro de editores de telemetria é feito ao chamar AddFeatureManagement(). Aqui está um exemplo configurando o gerenciamento de recursos para emitir telemetria com uma implementação de ITelemetryPublisher chamada MyTelemetryPublisher.

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

Distribuidor do Application Insights Telemetry

O pacote Microsoft.FeatureManagement.Telemetry.ApplicationInsights fornece uma implementação interna do editor de telemetria que envia dados de avaliação do sinalizador de recurso para o Application Insights. Para aproveitar isso, adicione uma referência ao pacote e registre o editor de telemetria do Application Insights, conforme mostrado abaixo.

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

Observação

O pacote base Microsoft.FeatureManagement não inclui esse editor de telemetria.

Um exemplo de seu uso pode ser encontrado no exemplo EvaluationDataToApplicationInsights.

Pré-requisito

Esse editor de telemetria depende do Application Insights já estar sendo configurado e registrado como um serviço de aplicativo. Por exemplo, isso é feito aqui no aplicativo de exemplo.

Cache

O estado do recurso é fornecido pelo sistema IConfiguration. Espera-se que qualquer cache e atualização dinâmica seja tratada pelos provedores de configuração. O gerenciador de recursos solicita a IConfiguration o valor mais recente do estado de um recurso sempre que um recurso é verificado para ser habilitado.

Instantâneo

Há cenários que exigem que o estado de um recurso permaneça consistente durante o tempo de vida de uma solicitação. Os valores retornados do IFeatureManager padrão poderão ser alterados se a origem IConfiguration da qual ele está sendo extraído for atualizada durante a solicitação. Isso pode ser evitado usando IFeatureManagerSnapshot. IFeatureManagerSnapshot pode ser recuperado da mesma maneira que IFeatureManager. IFeatureManagerSnapshot implementa a interface de IFeatureManager, mas armazena em cache o primeiro estado avaliado de um recurso durante uma solicitação e retorna o mesmo estado de um recurso durante seu tempo de vida.

Provedores de Recursos Personalizados

A implementação de um provedor de recursos personalizado permite que os desenvolvedores efetuem pull de sinalizadores de recursos de fontes como um banco de dados ou um serviço de gerenciamento de recursos. O provedor de recursos incluído que é usado por padrão puxa sinalizadores de recursos do sistema de configuração do .NET Core. Isso permite que os recursos sejam definidos em um arquivo appsettings.json ou em provedores de configuração, como a Configuração de Aplicativos do Azure. Esse comportamento pode ser substituído para fornecer controle completo de onde as definições de recurso são lidas.

Para personalizar o carregamento de definições de recurso, é necessário implementar a interface IFeatureDefinitionProvider.

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

    IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync();
}

Para usar uma implementação de IFeatureDefinitionProvider, ele deve ser adicionado à coleção de serviços antes de adicionar o gerenciamento de recursos. O exemplo a seguir adiciona uma implementação de IFeatureDefinitionProvider chamada InMemoryFeatureDefinitionProvider.

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

Próximas etapas

Para saber como usar sinalizadores de recursos em seus aplicativos, prossiga para os seguintes guias de início rápido.

Para saber como usar filtros de recursos, prossiga para os seguintes tutoriais.

Para saber como executar experimentos com sinalizadores de recursos variantes, prossiga para o tutorial a seguir.