Toggly.FeatureManagement
2.8.2
dotnet add package Toggly.FeatureManagement --version 2.8.2
NuGet\Install-Package Toggly.FeatureManagement -Version 2.8.2
<PackageReference Include="Toggly.FeatureManagement" Version="2.8.2" />
paket add Toggly.FeatureManagement --version 2.8.2
#r "nuget: Toggly.FeatureManagement, 2.8.2"
// Install Toggly.FeatureManagement as a Cake Addin #addin nuget:?package=Toggly.FeatureManagement&version=2.8.2 // Install Toggly.FeatureManagement as a Cake Tool #tool nuget:?package=Toggly.FeatureManagement&version=2.8.2
ASP.NET Core Feature Flags with Toggly
Feature flags provide a way for ASP.NET Core applications to turn features on or off dynamically. Developers can use feature flags in simple use cases like conditional statements to more advanced scenarios like conditionally adding routes or MVC filters. Feature flags build on top of the .NET Core configuration system. Any .NET Core configuration provider is capable of acting as the back-bone for feature flags.
Here are some of the benefits of using this library:
- Built on https://github.com/microsoft/FeatureManagement-Dotnet
- A common convention for feature management
- Feature Flag lifetime management
- Configuration values can change in real-time, feature flags can be consistent across the entire request
- Simple to Complex Scenarios Covered
- Toggle on/off features through declarative configuration file
- Dynamically evaluate state of feature based on call to server
- API extensions for ASP.NET Core and MVC framework
- Routing
- Filters
- Action Attributes
API Reference: https://go.microsoft.com/fwlink/?linkid=2091700
Feature Flags
Feature flags are composed of two parts, a name and a list of feature-filters that are used to turn the feature on.
Feature Filters
Feature filters define a scenario for when a feature should be enabled. When a feature is evaluated for whether it is on or off, its list of feature-filters are traversed until one of the filters decides the feature should be enabled. At this point the feature is considered enabled and traversal through the feature filters stops. If no feature filter indicates that the feature should be enabled, then it will be considered disabled.
As an example, a Microsoft Edge browser feature filter could be designed. This feature filter would activate any features it is attached to as long as an HTTP request is coming from Microsoft Edge.
Registration
The .NET Core TogglyFeatureProvider
is used to retrieve the latest feature flag configuration from toggly.
Referencing
To make it easier to reference these feature flags in code, we recommend to define feature flag variables like below.
// Define feature flags in an enum
public enum MyFeatureFlags
{
FeatureT,
FeatureU,
FeatureV
}
Service Registration
Feature flags rely on .NET Core dependency injection. We can register the feature management services using standard conventions.
using Microsoft.FeatureManagement;
using Microsoft.FeatureManagement.FeatureFilters;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddFeatureManagement()
.AddFeatureFilter<PercentageFilter>()
.AddFeatureFilter<TimeWindowFilter>();
}
}
Adding the Toggly Feature Provider
Add a section to your configuration provider, ex: appsettings.json (or environment variables, key vault, etc)
"Toggly": {
"AppKey": "[[toggly app key]]",
"Environment": "Production",
"BaseUrl": "https://app.toggly.io/"
}
In your Startup.cs, register the Toggly Feature Provider
builder.Services.AddOptions();
builder.Services.Configure<TogglySettings>(builder.Configuration.GetSection("Toggly"));
builder.Services.AddSingleton<IFeatureDefinitionProvider, TogglyFeatureProvider>();
Consumption
The simplest use case for feature flags is to do a conditional check for whether a feature is enabled to take different paths in code. The uses cases grow from there as the feature flag API begins to offer extensions into ASP.NET Core.
Feature Check
The basic form of feature management is checking if a feature is enabled and then performing actions based on the result. This is done through the IFeatureManager
's IsEnabledAsync
method.
IFeatureManager featureManager;
if (await featureManager.IsEnabledAsync(nameof(MyFeatureFlags.FeatureU)))
{
// Do something
}
Dependency Injection
When using the feature management library with MVC, the IFeatureManager
can be obtained through dependency injection.
public class HomeController : Controller
{
private readonly IFeatureManager _featureManager;
public HomeController(IFeatureManager featureManager)
{
_featureManager = featureManager;
}
}
Controllers and Actions
MVC controller and actions can require that a given feature, or one of any list of features, be enabled in order to execute. This can be done by using a FeatureGateAttribute
, which can be found in the Microsoft.FeatureManagement.Mvc
namespace.
[FeatureGate(MyFeatureFlags.FeatureX)]
public class HomeController : Controller
{
...
}
The HomeController
above is gated by "FeatureX". "FeatureX" must be enabled before any action the HomeController
contains can be executed.
[FeatureGate(MyFeatureFlags.FeatureY)]
public IActionResult Index()
{
return View();
}
The Index
MVC action above requires "FeatureY" to be enabled before it can execute.
Disabled Action Handling
When an MVC controller or action is blocked because none of the features it specifies are enabled, a registered IDisabledFeaturesHandler
will be invoked. By default, a minimalistic handler is registered which returns HTTP 404. This can be overridden using the IFeatureManagementBuilder
when registering feature flags.
public interface IDisabledFeaturesHandler
{
Task HandleDisabledFeature(IEnumerable<string> features, ActionExecutingContext context);
}
View
In MVC views <feature>
tags can be used to conditionally render content based on whether a feature is enabled or not.
<feature name=@nameof(MyFeatureFlags.FeatureX)>
<p>This can only be seen if 'FeatureX' is enabled.</p>
</feature>
The <feature>
tag requires a tag helper to work. This can be done by adding the feature management tag helper to the ViewImports.cshtml file.
@addTagHelper *, Microsoft.FeatureManagement.AspNetCore
MVC Filters
MVC action filters can be set up to conditionally execute based on the state of a feature. This is done by registering MVC filters in a feature aware manner.
The feature management pipeline supports async MVC Action filters, which implement IAsyncActionFilter
.
services.AddMvc(o =>
{
o.Filters.AddForFeature<SomeMvcFilter>(nameof(MyFeatureFlags.FeatureV));
});
The code above adds an MVC filter named SomeMvcFilter
. This filter is only triggered within the MVC pipeline if the feature it specifies, "FeatureV", is enabled.
Application building
The feature management library can be used to add application branches and middleware that execute conditionally based on feature state.
app.UseMiddlewareForFeature<ThirdPartyMiddleware>(nameof(MyFeatureFlags.FeatureU));
With the above call, the application adds a middleware component that only appears in the request pipeline if the feature "FeatureU" is enabled. If the feature is enabled/disabled during runtime, the middleware pipeline can be changed dynamically.
This builds off the more generic capability to branch the entire application based on a feature.
app.UseForFeature(featureName, appBuilder =>
{
appBuilder.UseMiddleware<T>();
});
Missing Feature Filters
If a feature is configured to be enabled for a specific feature filter and that feature filter hasn't been registered, then an exception will be thrown when the feature is evaluated. The exception can be disabled by using the feature management options.
services.Configure<FeatureManagementOptions>(options =>
{
options.IgnoreMissingFeatureFilters = true;
});
Using HttpContext
Feature filters can evaluate whether a feature should be enabled based off the properties of an HTTP Request. This is performed by inspecting the HTTP Context. A feature filter can get a reference to the HTTP Context by obtaining an IHttpContextAccessor
through dependency injection.
public class BrowserFilter : IFeatureFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public BrowserFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}
}
The IHttpContextAccessor
must be added to the dependency injection container on startup for it to be available. It can be registered in the IServiceCollection
using the following method.
public void ConfigureServices(IServiceCollection services)
{
...
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
...
}
Providing a Context For Feature Evaluation
In console applications there is no ambient context such as HttpContext
that feature filters can acquire and utilize to check if a feature should be on or off. In this case, applications need to provide an object representing a context into the feature management system for use by feature filters. This is done by using IFeatureManager.IsEnabledAsync<TContext>(string featureName, TContext appContext)
. The appContext object that is provided to the feature manager can be used by feature filters to evaluate the state of a feature.
MyAppContext context = new MyAppContext
{
AccountId = current.Id;
}
if (await featureManager.IsEnabledAsync(feature, context))
{
...
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 is compatible. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 is compatible. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETCoreApp 3.1
- Azure.Messaging.WebPubSub (>= 1.4.0)
- ConcurrentHashSet (>= 1.3.0)
- Google.Protobuf (>= 3.20.0)
- Grpc.Net.Client (>= 2.60.0)
- Grpc.Net.Client.Web (>= 2.60.0)
- Grpc.Net.ClientFactory (>= 2.60.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 3.1.0)
- Microsoft.Extensions.Http (>= 6.0.0)
- Microsoft.Extensions.Http.Polly (>= 6.0.0)
- Microsoft.FeatureManagement (>= 3.5.0)
- System.Net.Http.Json (>= 5.0.0)
- Websocket.Client (>= 5.1.2)
-
.NETStandard 2.1
- Azure.Messaging.WebPubSub (>= 1.4.0)
- ConcurrentHashSet (>= 1.3.0)
- Google.Protobuf (>= 3.20.0)
- Grpc.Net.Client (>= 2.60.0)
- Grpc.Net.Client.Web (>= 2.60.0)
- Grpc.Net.ClientFactory (>= 2.60.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 3.1.0)
- Microsoft.Extensions.Http (>= 6.0.0)
- Microsoft.Extensions.Http.Polly (>= 6.0.0)
- Microsoft.FeatureManagement (>= 3.5.0)
- System.Net.Http.Json (>= 5.0.0)
- Websocket.Client (>= 5.1.2)
-
net5.0
- Azure.Messaging.WebPubSub (>= 1.4.0)
- ConcurrentHashSet (>= 1.3.0)
- Google.Protobuf (>= 3.20.0)
- Grpc.Net.Client (>= 2.60.0)
- Grpc.Net.Client.Web (>= 2.60.0)
- Grpc.Net.ClientFactory (>= 2.60.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 3.1.0)
- Microsoft.Extensions.Http (>= 6.0.0)
- Microsoft.Extensions.Http.Polly (>= 6.0.0)
- Microsoft.FeatureManagement (>= 3.5.0)
- System.Net.Http.Json (>= 5.0.0)
- Websocket.Client (>= 5.1.2)
-
net6.0
- Azure.Messaging.WebPubSub (>= 1.4.0)
- ConcurrentHashSet (>= 1.3.0)
- Google.Protobuf (>= 3.20.0)
- Grpc.Net.Client (>= 2.60.0)
- Grpc.Net.Client.Web (>= 2.60.0)
- Grpc.Net.ClientFactory (>= 2.60.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 3.1.0)
- Microsoft.Extensions.Http (>= 6.0.0)
- Microsoft.Extensions.Http.Polly (>= 6.0.0)
- Microsoft.FeatureManagement (>= 3.5.0)
- System.Net.Http.Json (>= 5.0.0)
- Websocket.Client (>= 5.1.2)
-
net7.0
- Azure.Messaging.WebPubSub (>= 1.4.0)
- ConcurrentHashSet (>= 1.3.0)
- Google.Protobuf (>= 3.20.0)
- Grpc.Net.Client (>= 2.60.0)
- Grpc.Net.Client.Web (>= 2.60.0)
- Grpc.Net.ClientFactory (>= 2.60.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 3.1.0)
- Microsoft.Extensions.Http (>= 6.0.0)
- Microsoft.Extensions.Http.Polly (>= 6.0.0)
- Microsoft.FeatureManagement (>= 3.5.0)
- System.Net.Http.Json (>= 5.0.0)
- Websocket.Client (>= 5.1.2)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on Toggly.FeatureManagement:
Package | Downloads |
---|---|
Toggly.FeatureManagement.Storage.RavenDB
Provides a RavenDB implementation for feature snapshot storage. The application falls back to snapshot storage if it can't reach toggly.io |
|
Toggly.FeatureManagement.Web
Adds filters for Browser Family, Browser Language, Country, OS and User Claims and a default implementation for HTTP context targeting that support users and groups |
|
Toggly.Metrics.SystemMetrics
Toggly.Metrics.SystemMetrics extends the Toggly feature management library by adding support for capturing performance metrics. Capture performance counters, giving you valuable insights into the performance of your application. |
|
Toggly.FeatureManagement.Hangfire
Provides extensions to control hangfire jobs using feature flags via toggly.io |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
2.8.2 | 544 | 9/23/2024 |
2.8.1 | 2,242 | 5/30/2024 |
2.8.0 | 232 | 5/29/2024 |
2.7.6 | 261 | 5/28/2024 |
2.7.5 | 2,686 | 1/22/2024 |
2.7.4 | 1,207 | 11/27/2023 |
2.7.3 | 3,757 | 5/3/2023 |
2.7.2 | 1,960 | 3/3/2023 |
2.7.1 | 676 | 3/3/2023 |
2.7.0 | 689 | 3/3/2023 |
2.6.1 | 614 | 2/27/2023 |
2.6.0 | 1,011 | 2/13/2023 |
2.5.5 | 792 | 2/10/2023 |
2.5.4 | 1,085 | 2/5/2023 |
2.5.3 | 673 | 2/5/2023 |
2.5.2 | 776 | 1/31/2023 |
2.5.1 | 694 | 1/30/2023 |
2.5.0 | 693 | 1/30/2023 |
2.4.1 | 684 | 1/12/2023 |
2.4.0 | 691 | 1/9/2023 |
2.3.0 | 638 | 1/8/2023 |
2.2.11 | 664 | 1/4/2023 |
2.2.10 | 667 | 1/4/2023 |
2.2.9 | 653 | 1/4/2023 |
2.2.8 | 294 | 1/4/2023 |
2.2.7 | 298 | 1/4/2023 |
2.2.6 | 302 | 1/4/2023 |
2.2.5 | 681 | 1/4/2023 |
2.2.4 | 725 | 12/31/2022 |
2.2.3 | 1,055 | 12/29/2022 |
2.2.2 | 671 | 12/29/2022 |
2.2.1 | 679 | 12/29/2022 |
2.2.0 | 641 | 12/28/2022 |
2.1.19 | 615 | 11/29/2022 |
2.1.18 | 651 | 11/27/2022 |
2.1.17 | 565 | 11/27/2022 |
2.1.16 | 628 | 11/14/2022 |
2.1.15 | 2,553 | 8/19/2022 |
2.1.14 | 1,062 | 8/7/2022 |
2.1.13 | 836 | 8/7/2022 |
2.1.12 | 840 | 8/5/2022 |
2.1.11 | 1,855 | 7/22/2022 |
2.1.10 | 888 | 7/21/2022 |
2.1.9 | 1,017 | 7/14/2022 |
2.1.8 | 891 | 7/14/2022 |
2.1.7 | 891 | 7/7/2022 |
2.1.6 | 945 | 7/6/2022 |
2.1.5 | 1,010 | 6/20/2022 |
2.1.4 | 844 | 6/20/2022 |
2.1.3 | 869 | 6/19/2022 |
2.1.2 | 864 | 6/16/2022 |
2.1.1 | 844 | 6/16/2022 |
2.1.0 | 966 | 6/15/2022 |
2.0.2 | 910 | 6/9/2022 |
2.0.1 | 881 | 6/7/2022 |
2.0.0 | 832 | 6/5/2022 |
1.9.3 | 2,460 | 5/7/2022 |
1.9.2 | 967 | 5/4/2022 |
1.9.1 | 930 | 5/3/2022 |
1.9.0 | 922 | 5/3/2022 |
1.8.0 | 698 | 4/26/2022 |
1.7.3 | 726 | 4/24/2022 |
1.7.2 | 698 | 4/24/2022 |
1.7.1 | 685 | 4/24/2022 |
1.7.0 | 717 | 4/24/2022 |
1.6.5 | 566 | 4/23/2022 |
1.6.4 | 565 | 4/22/2022 |
1.6.3 | 1,407 | 3/30/2022 |
1.6.2 | 595 | 3/28/2022 |
1.6.1 | 564 | 3/27/2022 |
1.5.0 | 1,303 | 1/30/2022 |
1.4.1 | 489 | 1/16/2022 |
1.4.0 | 347 | 1/15/2022 |
1.3.0 | 368 | 1/14/2022 |
1.2.0 | 426 | 12/26/2021 |
1.1.0 | 377 | 12/22/2021 |
0.1.0 | 572 | 11/27/2022 |