| name | appcontext |
| description | USE FOR: Toggling runtime compatibility switches, enabling or disabling breaking-change mitigations between framework versions, gating legacy code paths at startup, and reading platform-level feature switches. DO NOT USE FOR: User-facing feature flags (use Microsoft.FeatureManagement or OpenFeature), per-request dynamic toggles, or configuration that changes at runtime without restart.
|
| license | MIT |
| metadata | {"displayName":"AppContext","author":"Tyler-R-Kendrick","version":"1.0.0"} |
| compatibility | ["claude","copilot","cursor"] |
| references | [{"title":"AppContext Class API Reference","url":"https://learn.microsoft.com/dotnet/api/system.appcontext"},{"title":".NET Runtime GitHub Repository","url":"https://github.com/dotnet/runtime"}] |
AppContext
Overview
System.AppContext provides a lightweight mechanism for setting and querying boolean switches that control runtime behavior across an application. It is built into the .NET base class library and requires no additional packages. The primary use case is framework compatibility: when a new .NET version introduces a breaking change, the runtime exposes an AppContext switch so that applications can opt in or opt out of the new behavior. Library authors can also define their own switches to let consumers toggle behaviors without recompiling.
Switches are static, process-wide, and typically set once at startup. They are not designed for dynamic feature flagging or per-user targeting. Values can be set in code, in the .runtimeconfig.json file, or via MSBuild properties that flow into that file at build time.
Setting Switches in Code
The simplest approach is to call AppContext.SetSwitch early in the application's entry point before any code reads the switch.
using System;
public static class Program
{
public static void Main(string[] args)
{
AppContext.SetSwitch("Switch.System.Net.DontEnableSchSendAuxRecord", true);
AppContext.SetSwitch("MyCompany.MyLib.UseLegacySerialization", false);
var host = CreateHostBuilder(args).Build();
host.Run();
}
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}
Reading Switches
Use AppContext.TryGetSwitch to read a switch value. It returns false if the switch has never been set, and outputs the switch value through the out parameter.
using System;
public sealed class SerializationService
{
private readonly bool _useLegacyFormat;
public SerializationService()
{
_useLegacyFormat = AppContext.TryGetSwitch(
"MyCompany.MyLib.UseLegacySerialization", out bool enabled) && enabled;
}
public byte[] Serialize<T>(T value)
{
if (_useLegacyFormat)
{
return LegacySerializer.Serialize(value);
}
return ModernSerializer.Serialize(value);
}
}
Setting Switches via runtimeconfig.json
For deployment scenarios where you cannot change source code, switches can be declared in the runtimeconfig.json file that ships alongside the application binary.
{
"runtimeOptions": {
"configProperties": {
"Switch.System.Net.DontEnableSchSendAuxRecord": true,
"MyCompany.MyLib.UseLegacySerialization": false,
"System.Globalization.Invariant": true
}
}
}
Setting Switches via MSBuild
You can also flow switch values from your .csproj file so they end up in runtimeconfig.json automatically.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<RuntimeHostConfigurationOption Include="MyCompany.MyLib.UseLegacySerialization"
Value="false"
Trim="true" />
</ItemGroup>
</Project>
Library Author Pattern
When authoring a library, define switch names as constants and document them for consumers.
using System;
namespace MyCompany.MyLib
{
public static class LibrarySwitches
{
public const string UseLegacySerialization = "MyCompany.MyLib.UseLegacySerialization";
public const string DisableConnectionPooling = "MyCompany.MyLib.DisableConnectionPooling";
public static bool IsLegacySerializationEnabled()
{
return AppContext.TryGetSwitch(UseLegacySerialization, out bool enabled) && enabled;
}
()
{
AppContext.TryGetSwitch(DisableConnectionPooling, enabled) && enabled;
}
}
}
AppContext vs Other Approaches
| Aspect | AppContext Switches | IConfiguration | Feature Management |
|---|
| Granularity | Process-wide boolean | Arbitrary key-value | Per-user, percentage, time-window |
| Mutability | Set once, read many | Supports reload | Dynamic evaluation per request |
| Configuration source | Code, runtimeconfig.json | JSON, env vars, secrets | Configuration + filters |
| Primary use case | Runtime compatibility | App settings | Feature flags and A/B testing |
| Package required | None (BCL) | Microsoft.Extensions.Configuration | Microsoft.FeatureManagement |
Best Practices
- Use a reverse-domain naming convention for custom switches (e.g.,
MyCompany.MyLib.SwitchName) to avoid collisions with framework or third-party switches.
- Set all switches as early as possible in
Main or Program.cs before any dependent code executes, because reads may be cached by the consuming component.
- Always provide a safe default when a switch is unset by treating
TryGetSwitch returning false as the default behavior path.
- Document every custom switch with its name, default value, and the behavioral change it controls so that consumers can discover it without reading source code.
- Expose switch names as
public const string fields in a dedicated static class rather than scattering magic strings throughout the codebase.
- Reserve
AppContext switches for binary compatibility decisions; do not use them for user-facing feature flags, A/B testing, or configuration that varies per environment.
- Prefer
runtimeconfig.json or MSBuild RuntimeHostConfigurationOption over SetSwitch in code when the switch needs to be controlled by operations teams without recompilation.
- Write unit tests that exercise both switch states by calling
AppContext.SetSwitch in test setup and verifying the two code paths produce correct results.
- Avoid reading a switch value on every method call in a hot path; instead, read it once during construction and store the result in a field.
- Review switches set by the .NET runtime itself when upgrading target frameworks, as new switches may be introduced that affect networking, globalization, or serialization behavior.