IFeatureManager from the Microsoft.FeatureManagement package manages feature flags in .NET.
It lets you choose the right branch of logic while the application is running.
Where Feature Flags Are Useful
-
Temporarily enabling or quickly disabling a feature—for example, during a new release or a hotfix.
-
Disabling problematic functionality during failures.
-
Running A/B tests.
-
Supporting temporary scenarios, such as promotions.
How It Works
Register Feature Management in the dependency injection container and add filters if needed. For example, TimeWindowFilter activates a flag only within a specific time range.
using Microsoft.FeatureManagement;
using Microsoft.FeatureManagement.FeatureFilters;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFeatureManagement()
// If you need a time window filter, add the following line
.AddFeatureFilter<TimeWindowFilter>();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new()
{
Title = "Feature Flags Demo API",
Version = "v1"
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.MapControllers();
app.Run();
Configure Flags in appsettings.json
Set the feature flag values in appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"FeatureManagement": {
"NewDashboard": true,
"HolidayPromo": {
"EnabledFor": [
{
"Name": "TimeWindow",
"Parameters": {
"Start": "2024-12-01T00:00:00Z",
"End": "2025-12-31T23:59:59Z"
}
}
]
}
}
}
Check a Flag with IFeatureManager
Check a flag’s state by injecting IFeatureManager and calling IsEnabledAsync:
using Microsoft.AspNetCore.Mvc;
using Microsoft.FeatureManagement;
namespace FeatureFlagsDemo.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class DashboardController(IFeatureManager featureManager) : ControllerBase
{
[HttpGet]
public async Task<IActionResult> GetDashboard()
{
var useNewDashboard =
await featureManager.IsEnabledAsync("NewDashboard");
if (useNewDashboard)
{
return Ok(new
{
Version = "New",
Data = GetNewDashboardData(),
});
}
return Ok(new
{
Version = "Legacy",
Data = GetLegacyDashboardData(),
});
}
[HttpGet("promo")]
public async Task<IActionResult> GetHolidayPromo()
{
if (!await featureManager.IsEnabledAsync("HolidayPromo"))
{
return Ok(new { Message = "No active promotions" });
}
return Ok(new
{
Message = "Holiday Special Offer!",
Discount = "50% OFF",
ValidUntil = "December 31, 2025",
PromoCode = "HOLIDAY2025"
});
}
private static object GetNewDashboardData() => new
{
Charts = new[]
{
new { Name = "Revenue Chart", Type = "Line", Interactive = true },
new { Name = "User Activity", Type = "Heatmap", Interactive = true },
new { Name = "Performance Metrics", Type = "Gauge", Interactive = true }
},
Layout = "Modern Grid",
Theme = "Dark Mode Available",
Features = new[]
{
"Real-time Updates",
"Custom Widgets",
"Export Options"
}
};
private static object GetLegacyDashboardData() => new
{
Charts = new[]
{
new { Name = "Basic Revenue", Type = "Bar", Interactive = false },
new { Name = "Simple Stats", Type = "Table", Interactive = false }
},
Layout = "Classic List",
Theme = "Light Only",
Features = new[] { "Static Reports" }
};
}
As a result, you can change application behavior through configuration without redeploying.
This approach saves time, reduces the risk of release errors, and gives the team more control over functionality directly in production.