EnumCraft.FeatureFlags.Json
1.0.1
dotnet add package EnumCraft.FeatureFlags.Json --version 1.0.1
NuGet\Install-Package EnumCraft.FeatureFlags.Json -Version 1.0.1
<PackageReference Include="EnumCraft.FeatureFlags.Json" Version="1.0.1" />
<PackageVersion Include="EnumCraft.FeatureFlags.Json" Version="1.0.1" />
<PackageReference Include="EnumCraft.FeatureFlags.Json" />
paket add EnumCraft.FeatureFlags.Json --version 1.0.1
#r "nuget: EnumCraft.FeatureFlags.Json, 1.0.1"
#:package EnumCraft.FeatureFlags.Json@1.0.1
#addin nuget:?package=EnumCraft.FeatureFlags.Json&version=1.0.1
#tool nuget:?package=EnumCraft.FeatureFlags.Json&version=1.0.1
EnumCraft.FeatureFlags.Json
JSON file provider for EnumCraft.FeatureFlags.
Overview
EnumCraft.FeatureFlags.Json is the JSON file-based data provider for the EnumCraft feature flag system. It loads feature flag configuration from a JSON file, supports per-tenant overrides, and automatically reloads when the file changes — using a FileSystemWatcher with a polling fallback for reliability.
This package is part of the EnumCraft ecosystem:
| Package | Description |
|---|---|
EnumCraft.Core |
Strongly-typed enums and flags (TypedEnum, TypedFlag) |
EnumCraft.Json |
JSON serialization extensions for TypedEnum and TypedFlag |
EnumCraft.FeatureFlags |
Feature flag abstraction and resolution engine |
EnumCraft.FeatureFlags.Json |
JSON file provider (this package) |
How It Works
JsonFeatureFlagProvider loads the JSON file once at startup and holds the result as an in-memory snapshot. All calls to IsActive and IsActive(tenantId) resolve against that snapshot — no file I/O occurs at query time.
When the file changes, the provider reloads it in the background and atomically swaps the snapshot. All subsequent reads see the new data. If the reload fails, the previous snapshot is retained — no partial reads, no dropped flags.
Querying is safe to call on every request. The per-call cost is a dictionary lookup.
Installation
dotnet add package EnumCraft.FeatureFlags.Json
Requirements
- .NET Standard 2.0+ or .NET 8.0+
EnumCraft.FeatureFlagsEnumCraft.Core
Quick Start
using EnumCraft.FeatureFlags.Json;
// Best Practice: name the json file to match its associated derived type
var options = new JsonFeatureFlagProviderOptions { DirectoryPath = AppDomain.CurrentDomain.BaseDirectory,
FileName = "AppFeatureFlag.json" };
var provider = new JsonFeatureFlagProvider(options);
if(!AppFeatureFlag.TryConfigure(provider, out var error))
{
Console.WriteLine($"Failed to configure flags: {error}");
return;
}
Optional Logging
Pass an ILogger to the constructor for diagnostic output. Falls back to NullLogger if not provided.
var logger = loggerFactory.CreateLogger<AppFeatureFlag>();
var provider = new JsonFeatureFlagProvider(options, logger);
You can also set a logger on the provider instance after construction:
provider.SetLogger(logger);
Configuration Options
| Property | Default | Description |
|---|---|---|
DirectoryPath |
(required) | Directory where the JSON file is located |
FileName |
"FeatureFlags.json" |
Name of the JSON file (best practice - name it to the associated derived type) |
EnableAutoReload |
true |
Watch for file changes and reload automatically |
DebounceMs |
350 |
Minimum ms between reloads, prevents rapid re-reads |
PollMs |
2000 |
Polling interval ms - fallback safety net alongside the watcher |
MaxRetryAttempts |
10 |
Max attempts to read file - handles mid-write states |
RetryDelayMs |
100 |
Delay ms between retry attempts |
FullPath is computed automatically from DirectoryPath + FileName.
JSON File Format
Name your JSON file to match your derived flag type (e.g., AppFeatureFlag.json).
{
"global": {
"DarkMode": true,
"NewDashboard": true,
"BetaReporting": false,
"MaintenanceMode": false
},
"tenants": {
"1001": {
"DarkMode": false,
"NewDashboard": false
},
"1002": {
"BetaReporting": true
}
}
}
Resolution Order
- Tenant override — per-tenant value, if present
- Global override — global value from JSON, if present
- Default — constructor-defined fallback (
isActiveDefault)
Key Casing
Flag keys in the JSON file must match the Code property of your flag definition exactly (case-sensitive). If you define your flags using nameof(), the key is the member name as written in C# — for example, "DarkMode", not "darkMode" or "dark_mode". A key mismatch silently falls through to the flag's constructor default with no warning.
Auto-Reload Behavior
When EnableAutoReload is true (the default):
- A
FileSystemWatchermonitors the JSON file for changes - A polling timer runs as a fallback safety net
- A debounce window (default 350ms) prevents rapid successive reloads
- On successful reload, the
Changedevent is raised and consumers can callGetSnapshot()for the latest data - On failed reload due to a transient IO error or mid-write corruption, the previous snapshot is retained — no partial reads
- The watcher is automatically recreated if it encounters a buffer overflow error
Emergency Shutoff
Deleting the JSON file reverts all flags to their isActiveDefault constructor values immediately. The Changed event fires on deletion so consumers are notified.
This is the recommended emergency shutoff pattern - no application restart required. Delete the file to revert all flags to their constructor-defined defaults. Flags with isActiveDefault: false will become inactive; flags with isActiveDefault: true will become active.
When the file is recreated, the provider detects the change and reloads the new values automatically.
Production Deployment
Multiple App Servers
Each running instance of your application loads and watches the JSON file independently. There is no cross-process coordination — each process manages its own in-memory snapshot.
For deployments with multiple app servers, point all instances at the same shared file:
- A shared network path (UNC path or mapped drive)
- A mounted volume shared across containers
- A managed file storage location (e.g., Azure Files, AWS EFS)
When the file is updated, each instance will detect the change and reload independently within its configured DebounceMs + PollMs window (worst-case: ~2.35 seconds at defaults).
⚠️ Write coordination is not provided. If multiple processes write to the same file simultaneously, last-write-wins. For v1.0.0, the JSON file is treated as read-only at runtime — updates are made out-of-band (deploy, copy, or manual edit).
Disposal
JsonFeatureFlagProvider implements IDisposable. At application shutdown, dispose the provider to stop the FileSystemWatcher and polling timer cleanly:
provider.Dispose();
After disposal, GetSnapshot() continues to return the last known snapshot — no exceptions thrown. The Changed event will not fire after disposal.
Framework Support
| Framework | Supported |
|---|---|
| .NET Framework 4.7.2+ | ✅ |
| .NET Standard 2.0+ | ✅ |
| .NET 8+ | ✅ |
Dependencies
- EnumCraft.Core (v1.0.0 or later)
- EnumCraft.FeatureFlags (v1.0.0 or later)
- System.Text.Json (v4.7.2 or later)
Known Limitations
- Key matching is case-sensitive. A key mismatch silently falls through to the flag's constructor default with no warning. Configurable case sensitivity is planned for a future release.
- No write coordination across processes. See Production Deployment.
Configure()may only be called once per flag type. UseTryConfigure()for a non-throwing alternative. Runtime provider swapping (Reconfigure()) is planned for a future release.
Related Packages
- EnumCraft.Core — Type-safe enumerations and flags
- EnumCraft.Json — JSON serialization extensions for TypedEnum and TypedFlag
- EnumCraft.FeatureFlags — Feature flag abstraction and resolution engine
License
MIT — see LICENSE for details.
Author
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. 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 was computed. 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 is compatible. 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- EnumCraft.FeatureFlags (>= 1.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 2.1.1)
- System.Text.Json (>= 4.7.2)
-
net8.0
- EnumCraft.FeatureFlags (>= 1.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 2.1.1)
- System.Text.Json (>= 4.7.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.1 | 94 | 4/26/2026 |
| 1.0.0 | 97 | 4/26/2026 |
| 0.3.1-preview | 99 | 4/22/2026 |
| 0.3.0-preview | 102 | 4/20/2026 |
| 0.2.2-preview | 102 | 4/6/2026 |
| 0.2.1-preview | 108 | 3/2/2026 |
| 0.2.0-preview | 104 | 2/24/2026 |