Skip to content
CØDE N!NJΔ edited this page Nov 3, 2025 · 9 revisions

FeatureOne - Complete Guide

Table of Contents

  1. Introduction
  2. What are Feature Toggles?
  3. Benefits of Feature Toggles
  4. Getting Started
  5. Core Concepts
  6. Architecture Overview
  7. Installation
  8. Basic Usage
  9. Dependency Injection Integration ⭐ NEW v5.1.0
  10. Storage Providers
  11. Condition Types
  12. Advanced Configuration
  13. Extending FeatureOne
  14. Best Practices
  15. Troubleshooting
  16. API Reference

Introduction

FeatureOne is a powerful .NET library designed to implement feature toggles (also known as feature flags) in your applications. With FeatureOne, you can control the visibility and behavior of application features at runtime without deploying new code, enabling safer releases, gradual rollouts, and better control over feature exposure.

This library supports .NET Framework 4.6.2, .NET Standard 2.1, and .NET 9.0, making it compatible with a wide range of .NET applications.

What are Feature Toggles?

A feature toggle (or feature flag) is a software engineering technique that allows you to turn application features "on" or "off" remotely without requiring a code deployment. This is achieved by wrapping new functionality in conditional statements that check the status of a toggle at runtime.

How Feature Toggles Work

varfeatureName="dashboard_widget";if(Features.Current.IsEnabled(featureName)){// New feature code hereShowDashboardWidget();}else{// Fallback or existing behaviorShowDefaultDashboard();}

The toggle status is determined by:

  • Storage Provider: Retrieves toggle configurations from your chosen storage medium
  • Conditions: Evaluate criteria based on user claims, environment, time, or custom logic
  • Operators: Combine multiple conditions using logical AND/OR operations

Benefits of Feature Toggles

1. Risk Mitigation

  • Instant Rollback: Disable problematic features immediately without code deployment
  • Gradual Rollout: Release features to small user groups first
  • A/B Testing: Compare different implementations with real users

2. Development Flexibility

  • Continuous Integration: Merge incomplete features safely using toggles
  • Decoupled Deployments: Deploy code and activate features independently
  • Environment-Specific Features: Show different features in different environments

3. Business Value

  • Faster Time-to-Market: Release features when business is ready, not just when code is ready
  • User Segmentation: Target features to specific user groups
  • Operational Control: Non-technical team members can control feature visibility

4. Quality Assurance

  • Production Testing: Test features with real data and real users safely
  • Canary Releases: Monitor feature performance with limited exposure
  • Blue-Green Deployments: Switch between different feature sets instantly

Getting Started

Prerequisites

  • .NET Framework 4.6.2+ or .NET Core 2.1+ or .NET 5.0+
  • Basic understanding of dependency injection (recommended)

Quick Start Example

// 1. Install the package// Install-Package FeatureOne.File// 2. Create a feature file (Features.json){"new_dashboard":{"toggle":{"conditions":[{"type":"simple","isEnabled": true
}]}}}// 3. Initialize FeatureOne (Traditional approach)varconfiguration=newFileConfiguration{FilePath=@"C:\path\to\Features.json"};varstorageProvider=newFileStorageProvider(configuration);Features.Initialize(()=>newFeatures(newFeatureStore(storageProvider)));// 4. Use in your codeif(Features.Current.IsEnabled("new_dashboard")){// Show new dashboard}

Core Concepts

Features

A Feature represents a piece of functionality that can be toggled on or off. Each feature has:

  • Name: Unique identifier for the feature
  • Toggle: Configuration that determines when the feature is enabled

Toggles

A Toggle contains the logic for determining feature enablement:

  • Operator: How to combine multiple conditions (AND/OR)
  • Conditions: Individual rules that evaluate to true/false

Conditions

Conditions are the building blocks of toggle logic:

  • SimpleCondition: Basic on/off switch
  • RegexCondition: Evaluates user claims against regular expressions
  • Custom Conditions: Implement ICondition for specific needs

Storage Providers

Storage Providers retrieve feature configurations from various sources:

  • FileStorageProvider: JSON files on disk
  • SQLStorageProvider: SQL databases
  • Custom Providers: Implement IStorageProvider for any data source

Architecture Overview

┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Application │───▶│ Features │───▶│ FeatureStore │
│ │ │ (Entry Point) │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ StorageProvider │
│ │
└─────────────────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ FileProvider │ │ SQLProvider │ │CustomProvider│
└──────────────┘ └──────────────────┘ └──────────────┘

Key Components

  1. Features: Main entry point for feature checking
  2. FeatureStore: Manages feature retrieval and caching
  3. StorageProvider: Abstracts data access
  4. Toggle: Contains evaluation logic
  5. Conditions: Individual evaluation rules
  6. Cache: Optional performance optimization

Installation

FeatureOne offers three NuGet packages based on your storage needs:

Core Package (Custom Storage)

Install-Package FeatureOne

Use when implementing custom storage providers.

SQL Storage Provider

Install-Package FeatureOne.SQL

Includes support for:

  • Microsoft SQL Server
  • SQLite
  • MySQL
  • PostgreSQL
  • ODBC/OleDB sources

File Storage Provider

Install-Package FeatureOne.File

Uses JSON files for feature storage.

Basic Usage

1. Simple Feature Toggle

// Configuration{"user_dashboard":{"toggle":{"conditions":[{"type":"simple","isEnabled": true
}]}}}// Usage
if (Features.Current.IsEnabled("user_dashboard")){returnView("NewDashboard");}returnView("OldDashboard");

2. User-Based Toggle

// Configuration{"admin_panel":{"toggle":{"conditions":[{"type":"regex","claim":"role","expression":"^administrator$"}]}}}// Usage
var claims =new Dictionary<string,string>{["role"]=user.Role,["email"]=user.Email};if(Features.Current.IsEnabled("admin_panel",claims)){ShowAdminPanel();}

3. Complex Toggle Logic

// Configuration - Feature enabled for admins OR beta users{"beta_feature":{"toggle":{"operator":"any","conditions":[{"type":"regex","claim":"role","expression":"^administrator$"},{"type":"regex","claim":"group","expression":"^beta_users$"}]}}}

Dependency Injection Integration ⭐ NEW v5.1.0

FeatureOne v5.1.0 introduces comprehensive dependency injection support for seamless integration with modern .NET applications.

Core Service Registration

// In Program.cs or Startup.csusingMicrosoft.Extensions.DependencyInjection;usingFeatureOne;publicvoidConfigureServices(IServiceCollectionservices){// Register FeatureOne with your storage provider using factory methodservices.AddFeatureOne(serviceProvider =>{// Create your storage provider implementation herevarstorageProvider=newYourStorageProviderImplementation();returnstorageProvider;});}

File Storage Provider Registration

// Register File Storage Provider with configurationservices.AddFeatureOneWithFileStorage(newFileConfiguration{FilePath="features.json",CacheSettings=newCacheSettings{EnableCache=true,Expiry=newExpiryPolicy{AbsoluteExpiration=TimeSpan.FromMinutes(5)}}});

SQL Storage Provider Registration

// Register SQL Storage Provider with configurationservices.AddFeatureOneWithSQLStorage(newSQLConfiguration{ConnectionSettings=newConnectionSettings{ConnectionString="Server=localhost;Database=FeatureToggles;Trusted_Connection=true;",ProviderName="System.Data.SqlClient"},CacheSettings=newCacheSettings{EnableCache=true,Expiry=newExpiryPolicy{AbsoluteExpiration=TimeSpan.FromMinutes(5)}}});

Advanced Registration Options

// Register File Storage with custom componentsservices.AddFeatureOneWithFileStorage(configuration:newFileConfiguration{FilePath="features.json"},deserializer:newCustomToggleDeserializer(),// Optional: Pass null to use defaultcache:newCustomCache()// Optional: Pass null to use default);// Register SQL Storage with custom componentsservices.AddFeatureOneWithSQLStorage(configuration:newSQLConfiguration{ConnectionSettings=newConnectionSettings{ConnectionString="connection_string",ProviderName="System.Data.SqlClient"}},deserializer:newCustomToggleDeserializer(),// Optional: Pass null to use defaultcache:newCustomCache()// Optional: Pass null to use default);

Usage in Controllers and Services

// Inject Features service directlypublicclassHomeController:Controller{privatereadonlyFeatures_features;publicHomeController(Featuresfeatures){_features=features;}publicIActionResultIndex(){if(_features.IsEnabled("new_dashboard")){returnView("NewDashboard");}returnView("OldDashboard");}}// Or inject in servicespublicclassUserService{privatereadonlyFeatures_features;publicUserService(Featuresfeatures){_features=features;}publicboolCanAccessPremiumFeatures(stringuserEmail){varclaims=newDictionary<string,string>{["email"]=userEmail};return_features.IsEnabled("premium_features",claims);}}

Service Lifetime

All FeatureOne services are registered with Singleton lifetime to ensure:

  • Consistent behavior across the application
  • Optimal performance with caching
  • Proper resource management

Complete Example: ASP.NET Core Integration ⭐ NEW v5.1.0

Here's a complete example showing how to integrate FeatureOne v5.1.0 with ASP.NET Core using dependency injection:

// Program.cs (ASP.NET Core 6+)usingMicrosoft.AspNetCore.Builder;usingMicrosoft.Extensions.DependencyInjection;usingMicrosoft.Extensions.Hosting;usingFeatureOne;usingFeatureOne.File.StorageProvider;varbuilder=WebApplication.CreateBuilder(args);// Add FeatureOne with File Storage Providerbuilder.Services.AddFeatureOneWithFileStorage(newFileConfiguration{FilePath="appsettings/features.json",CacheSettings=newCacheSettings{EnableCache=true,Expiry=newExpiryPolicy{AbsoluteExpiration=TimeSpan.FromMinutes(10)}}});// Add other servicesbuilder.Services.AddControllers();builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();varapp=builder.Build();// Configure pipelineif(app.Environment.IsDevelopment()){app.UseSwagger();app.UseSwaggerUI();}app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();app.Run();
// appsettings/features.json
{
"premium_features": {
"toggle": {
"operator": "any",
"conditions": [
{
"type": "Regex",
"claim": "subscription",
"expression": "^(premium|enterprise)$"
},
{
"type": "DateRange",
"startDate": "2025-10-01",
"endDate": "2025-12-31"
}
]
}
},
"admin_dashboard": {
"toggle": {
"conditions": [
{
"type": "Regex",
"claim": "role",
"expression": "^administrator$"
}
]
}
},
"beta_feature": {
"toggle": {
"conditions": [
{
"type": "Simple",
"isEnabled": true
}
]
}
}
}
// Controllers/DashboardController.csusingMicrosoft.AspNetCore.Mvc;usingFeatureOne;[ApiController][Route("api/[controller]")]publicclassDashboardController:ControllerBase{privatereadonlyFeatures_features;publicDashboardController(Featuresfeatures){_features=features;}[HttpGet("overview")]publicIActionResultGetOverview(){varuserClaims=GetUserClaims();varviewModel=newDashboardViewModel{ShowPremiumWidgets=_features.IsEnabled("premium_features",userClaims),ShowAdminPanel=_features.IsEnabled("admin_dashboard",userClaims),ShowBetaFeatures=_features.IsEnabled("beta_feature",userClaims)};returnOk(viewModel);}privateDictionary<string,string>GetUserClaims(){returnnewDictionary<string,string>{["role"]=User?.Identity?.Name??"guest",["subscription"]=GetUserSubscription(),["userId"]=User?.Identity?.Name?.GetHashCode().ToString()??"anonymous"};}privatestringGetUserSubscription(){// In a real application, this would come from your auth systemreturn"premium";// Example value}}publicclassDashboardViewModel{publicboolShowPremiumWidgets{get;set;}publicboolShowAdminPanel{get;set;}publicboolShowBetaFeatures{get;set;}}

This example demonstrates:

  • Modern ASP.NET Core integration with top-level statements
  • File-based feature storage with caching configuration
  • Multiple condition types including the new DateRangeCondition
  • Dependency injection of Features service into controllers
  • Real-world feature configuration with complex conditions
  • Proper service lifetime (Singleton) for optimal performance

Storage Providers

File Storage Provider

Perfect for smaller applications or when feature configurations change infrequently.

Setup

  1. Create Feature File (Features.json):
{
"feature_name": {
"toggle": {
"operator": "any",
"conditions": [
{
"type": "simple",
"isEnabled": true
}
]
}
}
}
  1. Configure Provider:
varconfiguration=newFileConfiguration{FilePath=@"C:\path\to\Features.json",CacheSettings=newCacheSettings{EnableCache=true,Expiry=newCacheExpiry{InMinutes=60,Type=CacheExpiryType.Absolute}}};varstorageProvider=newFileStorageProvider(configuration);Features.Initialize(()=>newFeatures(newFeatureStore(storageProvider)));

SQL Storage Provider

Ideal for enterprise applications requiring centralized feature management.

Database Setup

  1. Create Feature Table:
CREATETABLETFeatures (
Id INTNOT NULL IDENTITY PRIMARY KEY,
Name VARCHAR(255) NOT NULL,
Toggle NVARCHAR(4000) NOT NULL,
Archived BIT DEFAULT (0)
);
  1. Insert Feature Data:
INSERT INTO TFeatures (Name, Toggle, Archived) VALUES (
'dashboard_widget',
'{ "conditions":[{ "type":"Simple", "isEnabled": true }] }',
0
);
  1. Configure Provider:
// Register database providerDbProviderFactories.RegisterFactory("System.Data.SqlClient",SqlClientFactory.Instance);varsqlConfiguration=newSQLConfiguration{ConnectionSettings=newConnectionSettings{ProviderName="System.Data.SqlClient",ConnectionString="Data Source=server;Initial Catalog=Features;Integrated Security=SSPI;"},FeatureTable=newFeatureTable{TableName="TFeatures",NameColumn="Name",ToggleColumn="Toggle",ArchivedColumn="Archived"},CacheSettings=newCacheSettings{EnableCache=true,Expiry=newCacheExpiry{InMinutes=60}}};varstorageProvider=newSQLStorageProvider(sqlConfiguration);Features.Initialize(()=>newFeatures(newFeatureStore(storageProvider)));

Custom Storage Provider

Implement IStorageProvider for integration with APIs, NoSQL databases, or other data sources:

publicclassApiStorageProvider:IStorageProvider{privatereadonlyHttpClienthttpClient;publicApiStorageProvider(HttpClienthttpClient){this.httpClient=httpClient;}publicIFeature[]GetByName(stringname){// Fetch from REST APIvarresponse=httpClient.GetAsync($"api/features/{name}").Result;varjson=response.Content.ReadAsStringAsync().Result;// Deserialize and return features// Implementation depends on your API structure}}

Condition Types

Simple Condition

Basic on/off switch, independent of user context.

{
"type": "simple",
"isEnabled": true
}
newSimpleCondition{IsEnabled=true}

Regex Condition

Evaluates user claims against regular expressions.

{
"type": "regex",
"claim": "email",
"expression": "^[a-zA-Z0-9_.+-]+@company\\.com$"
}
newRegexCondition{Claim="email",Expression=@"^[a-zA-Z0-9_.+-]+@company\.com$"}

Common Regex Patterns

Email Domain Matching:

{
"claim": "email",
"expression": "@(company|partner)\\.com$"
}

Role-Based Access:

{
"claim": "role",
"expression": "^(admin|moderator)$"
}

User ID Ranges:

{
"claim": "userId",
"expression": "^[1-9][0-9]{3}$"
}

Custom Conditions

Create custom conditions by implementing ICondition. FeatureOne v5.1.0 also includes a built-in DateRangeCondition for time-based feature toggles.

publicclassTimeBasedCondition:ICondition{publicintStartHour{get;set;}=9;publicintEndHour{get;set;}=17;publicboolEvaluate(IDictionary<string,string>claims){varcurrentHour=DateTime.Now.Hour;returncurrentHour>=StartHour&&currentHour<=EndHour;}}

JSON Configuration:

{
"type": "TimeBased",
"startHour": 9,
"endHour": 17
}

Usage:

// Business hours featureif(Features.Current.IsEnabled("business_hours_feature")){// Only available during business hours}

Note: FeatureOne v5.1.0 includes a built-in DateRangeCondition for date-based feature toggles. See DateRangeCondition for more details.

DateRangeCondition ⭐ NEW v5.1.0

Enable features based on date ranges with the new DateRangeCondition. Perfect for scheduled feature releases, temporary promotions, or trial periods.

{
"type": "DateRange",
"startDate": "2025-01-01",
"endDate": "2025-12-31"
}
newDateRangeCondition{StartDate=newDateTime(2025,1,1),EndDate=newDateTime(2025,12,31)}

Flexible Date Range Configuration

Start Date Only (No end limit):

{
"type": "DateRange",
"startDate": "2025-06-01",
"endDate": null
}

End Date Only (No start limit):

{
"type": "DateRange",
"startDate": null,
"endDate": "2025-08-31"
}

Both Dates Null (Always enabled):

{
"type": "DateRange",
"startDate": null,
"endDate": null
}

Real-World Usage Examples

Seasonal Feature:

{
"holiday_promotion": {
"toggle": {
"conditions": [{
"type": "DateRange",
"startDate": "2025-11-01",
"endDate": "2025-12-31"
}]
}
}
}

Beta Testing Period:

{
"beta_feature": {
"toggle": {
"conditions": [{
"type": "DateRange",
"startDate": "2025-10-01",
"endDate": "2025-12-31"
}]
}
}
}

Usage in Code:

// Feature enabled during holiday seasonif(Features.Current.IsEnabled("holiday_promotion")){// Show special holiday offersShowHolidayPromotions();}// Feature enabled during beta testingif(Features.Current.IsEnabled("beta_feature")){// Show beta features to testersShowBetaFeatures();}

Advanced Configuration

Caching Configuration

Optimize performance with intelligent caching:

varcacheSettings=newCacheSettings{EnableCache=true,Expiry=newCacheExpiry{InMinutes=30,Type=CacheExpiryType.Sliding// Reset timer on access}};

Cache Types:

  • Absolute: Cache expires after fixed time
  • Sliding: Cache expires after inactivity period

Logging Configuration

Monitor feature toggle behavior:

publicclassCustomLogger:IFeatureLogger{privatereadonlyILogger<CustomLogger>logger;publicCustomLogger(ILogger<CustomLogger>logger){this.logger=logger;}publicvoidInfo(stringmessage)=>logger.LogInformation(message);publicvoidDebug(stringmessage)=>logger.LogDebug(message);publicvoidWarn(stringmessage)=>logger.LogWarning(message);publicvoidError(stringmessage,Exceptionex)=>logger.LogError(ex,message);}// Register with FeatureOnevarcustomLogger=newCustomLogger(serviceProvider.GetService<ILogger<CustomLogger>>());Features.Initialize(()=>newFeatures(newFeatureStore(storageProvider,customLogger),customLogger));

Multiple Database Providers

FeatureOne supports various database providers:

// SQL ServerDbProviderFactories.RegisterFactory("System.Data.SqlClient",SqlClientFactory.Instance);// MySQLDbProviderFactories.RegisterFactory("MySql.Data.MySqlClient",MySqlClientFactory.Instance);// PostgreSQLDbProviderFactories.RegisterFactory("Npgsql",NpgsqlFactory.Instance);// SQLiteDbProviderFactories.RegisterFactory("System.Data.SQLite",SQLiteFactory.Instance);

Extending FeatureOne

Custom Condition Implementation

publicclassPercentageRolloutCondition:ICondition{publicintPercentage{get;set;}publicboolEvaluate(IDictionary<string,string>claims){if(!claims.TryGetValue("userId",outstringuserIdStr))returnfalse;if(!int.TryParse(userIdStr,outintuserId))returnfalse;// Use user ID to determine consistent rolloutvarhash=userId.GetHashCode();varbucket=Math.Abs(hash%100);returnbucket<Percentage;}}

Usage Example:

{
"new_checkout": {
"toggle": {
"conditions": [{
"type": "PercentageRollout",
"percentage": 25
}]
}
}
}

Custom Cache Implementation

publicclassRedisCacheProvider:ICache{privatereadonlyIConnectionMultiplexerredis;privatereadonlyIDatabasedatabase;publicRedisCacheProvider(IConnectionMultiplexerredis){this.redis=redis;this.database=redis.GetDatabase();}publicvoidAdd(stringkey,objectvalue,CacheItemPolicypolicy){varjson=JsonSerializer.Serialize(value);varexpiry=policy.AbsoluteExpiration.HasValue?policy.AbsoluteExpiration.Value.TimeOfDay:policy.SlidingExpiration;database.StringSet(key,json,expiry);}publicobjectGet(stringkey){varjson=database.StringGet(key);returnjson.HasValue?JsonSerializer.Deserialize<object>(json):null;}}

Custom Toggle Deserializer

For complex toggle requirements:

publicclassCustomToggleDeserializer:IToggleDeserializer{publicIToggleDeserialize(stringtoggle){// Custom deserialization logic// Handle special toggle formats// Support additional operators}}

Best Practices

1. Feature Toggle Naming

Good Names:

user_dashboard_v2
checkout_flow_redesign
mobile_payment_integration

Avoid:

feature1
test_toggle
temp_fix

2. Toggle Lifecycle Management

publicclassFeatureToggleAudit{// Track toggle creation datepublicDateTimeCreatedDate{get;set;}// Review toggles older than 6 monthspublicboolRequiresReview=>DateTime.Now.Subtract(CreatedDate).Days>180;}

3. Testing Strategies

Unit Testing:

[Test]publicvoidShould_Enable_Feature_For_Admin_Users(){// Arrangevarclaims=newDictionary<string,string>{["role"]="administrator"};// ActvarisEnabled=Features.Current.IsEnabled("admin_feature",claims);// AssertAssert.IsTrue(isEnabled);}

Integration Testing:

[Test]publicvoidShould_Load_Features_From_Database(){// Test storage provider integration// Verify cache behavior// Test error handling}

4. Performance Considerations

  • Enable Caching: Always use caching for production
  • Monitor Cache Hit Rates: Track cache effectiveness
  • Optimize Database Queries: Index feature name columns
  • Batch Feature Checks: Check multiple features in one call when possible

5. Security Considerations

// Sanitize user inputspublicboolIsFeatureEnabledForUser(stringfeatureName,ClaimsPrincipaluser){// Validate feature nameif(string.IsNullOrWhiteSpace(featureName))returnfalse;// Extract only necessary claimsvarsafeClaims=ExtractSafeClaims(user);returnFeatures.Current.IsEnabled(featureName,safeClaims);}

6. Monitoring and Alerting

publicclassFeatureToggleMetrics{publicvoidRecordFeatureCheck(stringfeatureName,boolisEnabled,TimeSpanduration){// Log to metrics system// Alert on performance issues// Track feature usage}}

Troubleshooting

Common Issues

1. Features Always Return False

Problem: Features.Current.IsEnabled() always returns false.

Solutions:

  • Verify Features.Initialize() was called
  • Check storage provider configuration
  • Verify feature exists in storage
  • Check condition syntax

2. Database Connection Issues

Problem: SQL storage provider throws connection errors.

Solutions:

try{varfeatures=storageProvider.GetByName("test_feature");}catch(Exceptionex){logger.Error("Storage provider error",ex);// Handle gracefully - return default behavior}

3. Cache Not Working

Problem: Changes to feature configurations not reflected immediately.

Solutions:

  • Verify cache settings
  • Check file change monitoring (for file provider)
  • Consider cache invalidation strategy

4. Regex Conditions Not Matching

Problem: Regex conditions not evaluating correctly.

Solutions:

  • Test regex patterns separately
  • Use online regex testing tools
  • Check claim values are correct
  • Verify case sensitivity

Debugging Tips

// Enable detailed loggingvarlogger=newCustomLogger();Features.Initialize(()=>newFeatures(newFeatureStore(storageProvider,logger),logger));// Test feature evaluationvartestClaims=newDictionary<string,string>{["email"]="test@company.com",["role"]="user"};varresult=Features.Current.IsEnabled("test_feature",testClaims);logger.Info($"Feature 'test_feature' evaluated to: {result}");

Performance Debugging

publicclassPerformanceAwareFeatures{privatereadonlyStopwatchstopwatch=newStopwatch();publicboolIsEnabled(stringfeatureName,IDictionary<string,string>claims){stopwatch.Restart();varresult=Features.Current.IsEnabled(featureName,claims);stopwatch.Stop();if(stopwatch.ElapsedMilliseconds>100){logger.Warn($"Slow feature check: {featureName} took {stopwatch.ElapsedMilliseconds}ms");}returnresult;}}

API Reference

Core Classes

Features Class

publicclassFeatures{publicstaticFeaturesCurrent{get;privateset;}// Initialize FeatureOnepublicstaticvoidInitialize(Func<Features>factory);// Check if feature is enabledpublicboolIsEnabled(stringname);publicboolIsEnabled(stringname,ClaimsPrincipalprincipal);publicboolIsEnabled(stringname,IEnumerable<Claim>claims);publicboolIsEnabled(stringname,IDictionary<string,string>claims);}

IStorageProvider Interface

publicinterfaceIStorageProvider{IFeature[]GetByName(stringname);}

ICondition Interface

publicinterfaceICondition{boolEvaluate(IDictionary<string,string>claims);}

Configuration Classes

// File StoragepublicclassFileConfiguration{publicstringFilePath{get;set;}publicCacheSettingsCacheSettings{get;set;}}// SQL StoragepublicclassSQLConfiguration{publicConnectionSettingsConnectionSettings{get;set;}publicFeatureTableFeatureTable{get;set;}publicCacheSettingsCacheSettings{get;set;}}// Cache SettingspublicclassCacheSettings{publicboolEnableCache{get;set;}publicCacheExpiryExpiry{get;set;}}// DateRangeCondition ⭐ NEW v5.1.0publicclassDateRangeCondition:ICondition{publicDateTime?StartDate{get;set;}publicDateTime?EndDate{get;set;}publicboolEvaluate(IDictionary<string,string>claims){varnow=DateTime.Now.Date;if(StartDate.HasValue&&now<StartDate.Value.Date)returnfalse;if(EndDate.HasValue&&now>EndDate.Value.Date)returnfalse;returntrue;}}

Extension Points

  • ICondition: Custom toggle conditions (includes built-in DateRangeCondition ⭐ NEW v5.1.0)
  • IStorageProvider: Custom storage backends
  • IFeatureLogger: Custom logging implementations
  • ICache: Custom caching providers
  • IConditionDeserializer: Custom condition deserialization
  • IToggleDeserializer: Custom toggle deserialization

Dependency Injection Extensions ⭐ NEW v5.1.0

FeatureOne Core Extensions

publicstaticclassFeatureOneServiceCollectionExtensions{publicstaticIServiceCollectionAddFeatureOne(thisIServiceCollectionservices,Func<IServiceProvider,IStorageProvider>storageProviderFactory);}

FeatureOne.File Extensions

publicstaticclassFeatureOneFileExtensions{publicstaticIServiceCollectionAddFeatureOneWithFileStorage(thisIServiceCollectionservices,FileConfigurationconfiguration,IToggleDeserializerdeserializer=null,ICachecache=null);}

FeatureOne.SQL Extensions

publicstaticclassFeatureOneSQLExtensions{publicstaticIServiceCollectionAddFeatureOneWithSQLStorage(thisIServiceCollectionservices,SQLConfigurationconfiguration,IToggleDeserializerdeserializer=null,ICachecache=null);}

What's New in v5.1.0 ⭐

Security Enhancements

  • RegexCondition ReDoS Protection: Added timeout validation to prevent Regular Expression Denial of Service attacks
  • Secure Dynamic Type Loading: Replaced assembly scanning with explicit safe type registry to prevent unsafe type loading

Architecture Improvements

  • Actual Prefix Matching: Fixed FindStartsWith implementation to properly support prefix matching instead of exact matching
  • Dependency Injection Patterns: Implemented proper dependency injection patterns with constructors that accept dependencies explicitly

New Features

  • DateRangeCondition: Added new condition type for time-based feature toggles
  • Configuration Validation: Added configuration validation system for feature names and condition parameters

Dependency Injection Integration

  • Core Service Registration: Added AddFeatureOne extension method for registering FeatureOne services with Microsoft.Extensions.DependencyInjection using factory pattern
  • File Storage Provider Registration: Added AddFeatureOneWithFileStorage extension method for easy FileStorageProvider registration
  • SQL Storage Provider Registration: Added AddFeatureOneWithSQLStorage extension method for easy SQLStorageProvider registration

Quality Improvements

  • Comprehensive Test Coverage: Achieved 90%+ code coverage across all critical components
  • Integration Testing: Complete end-to-end validation of all components

Breaking Changes

  • External Condition Types: External condition types from other assemblies may no longer be loadable (security enhancement)
  • Configuration Validation: May detect previously undetected configuration errors

Putting It All Together: v5.1.0 Feature Showcase ⭐ NEW

Here's a comprehensive example showcasing all the new features in FeatureOne v5.1.0:

// Program.cs - Modern ASP.NET Core integration with DIusingMicrosoft.AspNetCore.Builder;usingMicrosoft.Extensions.DependencyInjection;usingMicrosoft.Extensions.Hosting;usingFeatureOne;usingFeatureOne.File.StorageProvider;varbuilder=WebApplication.CreateBuilder(args);// Register FeatureOne with comprehensive configurationbuilder.Services.AddFeatureOneWithFileStorage(newFileConfiguration{FilePath="app_data/features.json",CacheSettings=newCacheSettings{EnableCache=true,Expiry=newExpiryPolicy{AbsoluteExpiration=TimeSpan.FromMinutes(15),SlidingExpiration=TimeSpan.FromMinutes(5)}}});varapp=builder.Build();// API endpoint demonstrating all new v5.1.0 featuresapp.MapGet("/api/features",(Featuresfeatures,HttpContextcontext)=>{varuserClaims=newDictionary<string,string>{["role"]=context.User?.FindFirst("role")?.Value??"guest",["email"]=context.User?.FindFirst("email")?.Value??"",["subscription"]=GetUserSubscription(context),// premium, basic, trial, etc.["userId"]=context.User?.FindFirst("sub")?.Value??""};// Demonstrate all new v5.1.0 featuresvarfeatureStatus=new{// New DateRangeConditionseasonalPromotion=features.IsEnabled("seasonal_promotion",userClaims),// Security-enhanced RegexCondition with ReDoS protectionenterpriseEmail=features.IsEnabled("enterprise_email_access",userClaims),// Traditional simple togglenewDashboard=features.IsEnabled("new_dashboard",userClaims),// Complex toggle with multiple conditionspremiumFeatures=features.IsEnabled("premium_features",userClaims)};returnResults.Ok(featureStatus);});app.Run();stringGetUserSubscription(HttpContextcontext){// Implementation would check user's subscription statusreturn"premium";// Example}
// app_data/features.json - Feature configuration showcasing v5.1.0 features
{
"seasonal_promotion": {
"description": "Holiday promotion active Dec 1-31",
"toggle": {
"conditions": [{
"type": "DateRange",
"startDate": "2025-12-01",
"endDate": "2025-12-31"
}]
}
},
"enterprise_email_access": {
"description": "Enterprise email domain access with ReDoS protection",
"toggle": {
"conditions": [{
"type": "Regex",
"claim": "email",
"expression": "^[a-zA-Z0-9._%+-]+@(enterprise|company)\\.com$"
}]
}
},
"premium_features": {
"description": "Premium subscription features with beta access",
"toggle": {
"operator": "any",
"conditions": [
{
"type": "Regex",
"claim": "subscription",
"expression": "^(premium|enterprise)$"
},
{
"type": "DateRange",
"startDate": "2025-11-01",
"endDate": "2025-12-31"
}
]
}
},
"new_dashboard": {
"description": "New dashboard UI",
"toggle": {
"conditions": [{
"type": "Simple",
"isEnabled": true
}]
}
}
}

This showcase demonstrates:

  • DateRangeCondition: Time-based feature toggles for seasonal promotions
  • Enhanced Security: ReDoS protection in RegexCondition
  • Modern DI Integration: Seamless ASP.NET Core integration
  • Comprehensive Caching: Advanced cache configuration
  • Real-world Scenarios: Practical feature toggle implementations
  • Backward Compatibility: Works with existing simple toggles

Conclusion

FeatureOne provides a robust, flexible foundation for implementing feature toggles in .NET applications. Whether you're building a simple web application or a complex enterprise system, FeatureOne's extensible architecture adapts to your needs.

Key Takeaways:

  • Start simple with basic toggles, then add complexity as needed
  • Choose the right storage provider for your infrastructure
  • Implement proper monitoring and logging
  • Plan for toggle lifecycle management
  • Test your feature toggle logic thoroughly

For additional help and community support, visit the GitHub repository or review the unit tests for comprehensive usage examples.

Happy feature toggling! 🚀

Clone this wiki locally