State Change Handlers
Learn how to react to feature flag state changes in your application.
IFeatureStateService
IFeatureStateService allows registering Action handlers which will be called when the feature changes state.
Callbacks follow environment-level AlwaysOn/off state. A percentage, user-targeting, or entity rule is treated as off for these callbacks, even if it evaluates true for a particular request. Use request evaluation for per-user decisions.
Available callbacks
WhenFeatureTurnsOn(string/object featureKey, Action action): runs when a feature flips on (immediately if already on). Returns a Guid for unregistering.WhenFeatureTurnsOff(string/object featureKey, Action action): runs when a feature flips off (immediately if already off). Returns a Guid for unregistering.UnregisterFeatureStateChange(string featureKey, Guid id): removes a previously registered on/off handler.WhenDefinitionsChange(Action action): runs when feature definitions are refreshed (HTTP refresh, snapshot load, or live update).UnregisterDefinitionsChange(Guid id): removes a definitions-change handler.
Examples
Turn on/off handlers
var featureStateService = app.Services.GetRequiredService<IFeatureStateService>();
// Subscribe
var onId = featureStateService.WhenFeatureTurnsOn(FeatureFlags.HourlyJob, () =>
{
RecurringJob.AddOrUpdate<ITestRecurringJob>("Hourly job", s => s.RunAsync(), Cron.Hourly());
});
var offId = featureStateService.WhenFeatureTurnsOff(FeatureFlags.HourlyJob, () =>
{
RecurringJob.RemoveIfExists("Hourly job");
});
// Unsubscribe (e.g., during disposal)
featureStateService.UnregisterFeatureStateChange(FeatureFlags.HourlyJob.ToString(), onId);
featureStateService.UnregisterFeatureStateChange(FeatureFlags.HourlyJob.ToString(), offId);
Definitions change handler
var featureStateService = app.Services.GetRequiredService<IFeatureStateService>();
var defId = featureStateService.WhenDefinitionsChange(() =>
{
// Clear caches, refresh generated docs, etc.
logger.LogInformation("Feature definitions changed. Refreshing dependent resources.");
});
// Unsubscribe when no longer needed
featureStateService.UnregisterDefinitionsChange(defId);
Feature flags around Hangfire jobs
Install Toggly.FeatureManagement.Hangfire and configure Hangfire storage with AddHangfire first. The extension resolves IRecurringJobManager from dependency injection and keeps the recurring job registered only while the flag has AlwaysOn state:
using Hangfire;
using Toggly.FeatureManagement;
using Toggly.FeatureManagement.HangfireExtensions;
var featureStateService = app.Services.GetRequiredService<IFeatureStateService>();
featureStateService.AddOrUpdateJob<ITestRecurringJob>(
app.Services,
"hourly-job",
"Hourly job", // Stable Hangfire recurring job ID.
job => job.RunAsync(),
Cron.Hourly());
Register this once during application startup, not inside a request. Turning the flag off removes future recurring scheduling; it does not cancel a job already running. A configured Hangfire server is needed to execute jobs; registering a recurring job alone does not start a worker.
Working example
Explore the .NET SDK Sample. Its guided sections include the first flag, request identity, Order VIP targeting, filter presets, named variants, Hangfire, health checks, and OpenAPI filtering. The README maps each section to its source files and includes a manual walkthrough.