Lockjaw.Client
2.0.2.17
See the version list below for details.
dotnet add package Lockjaw.Client --version 2.0.2.17
NuGet\Install-Package Lockjaw.Client -Version 2.0.2.17
<PackageReference Include="Lockjaw.Client" Version="2.0.2.17" />
<PackageVersion Include="Lockjaw.Client" Version="2.0.2.17" />
<PackageReference Include="Lockjaw.Client" />
paket add Lockjaw.Client --version 2.0.2.17
#r "nuget: Lockjaw.Client, 2.0.2.17"
#:package Lockjaw.Client@2.0.2.17
#addin nuget:?package=Lockjaw.Client&version=2.0.2.17
#tool nuget:?package=Lockjaw.Client&version=2.0.2.17
Lockjaw.Client
Read the environment currently selected in the Lockjaw app from any .NET application, and react the moment it changes.
Lockjaw keeps every environment's values in one configuration — keys prefixed Development_,
Production_, or whatever else you declare — and lets you choose, per setting and while your app is
running, which one each resolves to. This package is the reading half.
using Lockjaw.Client;
var apiUrl = LockjawClient.Default.Get("ApiUrl");
var db = LockjawClient.Default.GetConnectionString("Main");
Ask for the key without its prefix; you get whichever environment is currently selected. No configuration on your side — the client and the app find each other by instance name.
Finding your instance
One Lockjaw manages several applications, each published under its own instance name. Name the one you want:
var client = new LockjawClient(new LockjawClientOptions { InstanceName = "OrderService" });
Or let the client find it. Lockjaw publishes the application names each instance serves, so a client can match its own and needs no configuration at all:
// In OrderService.exe — no instance name anywhere.
var client = new LockjawClient(new LockjawClientOptions { AutoDiscoverInstance = true });
Console.WriteLine(client.InstanceName); // "OrderService", resolved at construction
ApplicationName defaults to the entry assembly's name; set it explicitly if your executable is
named differently from the instance's target. Discovery is off by default and never overrides an
explicit choice: an explicit SnapshotPath or a non-default InstanceName wins, and if nothing
matches the client falls back to default.
To see what is available:
foreach (var instance in SnapshotStore.EnumerateInstances())
Console.WriteLine($"{instance.Instance}: {string.Join(", ", instance.Applications)}");
netstandard2.0, so it works from .NET Framework 4.6.1+ and .NET Core / .NET 5+.
Typed reads
var timeout = LockjawClient.Default.Get("TimeoutSeconds", 30); // parsed, with a fallback
var url = LockjawClient.Default.GetRequired("ApiUrl"); // throws if missing
Colon-delimited paths
Nested JSON flattens into colon paths, the same convention .NET configuration uses, so ask for a nested value by its path:
var level = LockjawClient.Default.Get("Logging:Level");
var host = LockjawClient.Default.Get("Hosts:0"); // arrays take their index
The environment prefix can sit on any segment of the path, which is what lets a single leaf vary while its siblings stay shared:
{
// The whole subtree varies:
"Development_Logging": { "Level": "Debug", "Console": { "Enabled": true } },
"Production_Logging": { "Level": "Warning", "Console": { "Enabled": false } },
// …or just one leaf, with its siblings shared:
"Telemetry": {
"Development_Endpoint": "http://localhost:4317",
"Production_Endpoint": "https://otel.contoso.com",
"SampleRate": "0.25"
}
}
Both forms give you Logging:Level, Logging:Console:Enabled and Telemetry:Endpoint — ask for the
path with the prefix removed, exactly as you would for a flat key. Connection strings nest the same
way, and a flat key that simply contains a colon (Development_Api:Url in app.config) works too.
Reacting to changes
Changes are picked up live. The event arrives on a background file-watcher thread, so a WPF or
WinForms handler must reach the UI thread first — SubscribeOnUiThread does that for you when
called from the UI thread, and returns a token you dispose to unsubscribe:
_subscription = LockjawClient.Default.SubscribeOnUiThread((s, e) =>
{
ApiUrlText.Text = e.Value; // already on the dispatcher
});
Under the hood it captures the UI thread's SynchronizationContext — a
DispatcherSynchronizationContext on WPF — so this package needs no WPF reference of its own. For a
context you hold yourself, use SubscribeOn(context, handler). Off the UI thread, subscribe to
SettingChanged directly.
When the Lockjaw app isn't running
- Values stay available once the app has run once — the client reads the last published values.
- If nothing was ever published, the client falls back to its own
app.config/appsettings.json, read with the same prefix convention. So an app that references Lockjaw.Client still works standalone. - Published values always win, per key; the fallback only fills gaps.
SourceOf(key)tells you which it was:Published,LocalConfig, orNone.
switch (LockjawClient.Default.SourceOf("ApiUrl"))
{
case LockjawValueSource.Published: /* the user picked this in the app */ break;
case LockjawValueSource.LocalConfig: /* app not running; from my own config */ break;
case LockjawValueSource.None: /* nobody has this key */ break;
}
Your own fallback config
Ship the settings in your application's own appsettings.json and it keeps working on a machine
where Lockjaw has never run. Same prefix convention:
{
// No "Lockjaw" section — which environments exist is the Lockjaw app's business.
"AppSettings": {
"Local_ApiUrl": "http://localhost:5000/api",
"Development_ApiUrl": "https://localhost:5001/api",
"Production_ApiUrl": "https://api.contoso.com",
// Partial coverage is fine: with no Local value, Local resolves to Development.
"Development_LogLevel": "Debug",
"Production_LogLevel": "Warning",
// Unprefixed keys are served too, so ordinary settings keep working.
"SupportEmail": "support@contoso.com"
},
"ConnectionStrings": {
"Local_Main": "Server=(localdb)\\MSSQLLocalDB;Database=Contoso_Local",
"Development_Main": "Server=(localdb)\\MSSQLLocalDB;Database=Contoso_Dev",
"Production_Main": "Server=sql-prod-01;Database=Contoso"
}
}
Or in app.config:
<configuration>
<appSettings>
<add key="Local_ApiUrl" value="http://localhost:5000/api" />
<add key="Development_ApiUrl" value="https://localhost:5001/api" />
<add key="Production_ApiUrl" value="https://api.contoso.com" />
<add key="SupportEmail" value="support@contoso.com" />
</appSettings>
<connectionStrings>
<add name="Development_Main"
connectionString="Server=(localdb)\MSSQLLocalDB;Database=Contoso_Dev" />
<add name="Production_Main"
connectionString="Server=sql-prod-01;Database=Contoso" />
</connectionStrings>
</configuration>
Three things differ from the Lockjaw app's own configuration:
- No
Lockjawsection. Which environments exist is the app's business. The client needs the list only when its own keys use prefixes beyondDevelopment_/Production_. - Unprefixed keys are served, where the Lockjaw window hides them — so your ordinary settings can live in the same file, untouched.
- Prefixed keys resolve to
FallbackEnvironment, which defaults toDevelopment, so a machine without Lockjaw never quietly starts on production.
Anything beyond the defaults is set in code rather than in the file:
using var client = new LockjawClient(new LockjawClientOptions
{
// Only needed for prefixes beyond the built-in pair.
Environments = new List<EnvironmentDefinition>
{
new EnvironmentDefinition("Local", "Local_"),
new EnvironmentDefinition("Development", "Development_"),
new EnvironmentDefinition("Production", "Production_")
},
FallbackEnvironment = "Local", // which one the fallback picks
IncludeUnprefixedFallback = true, // serve plain keys too (the default)
FallBackToLocalConfig = true // false: require the Lockjaw app
});
Values the Lockjaw app publishes carry their environment name with them, so none of this affects those — it shapes the fallback only.
API
| Member | Purpose |
|---|---|
Get(key) / Get<T>(key, fallback) / GetRequired(key) |
Resolve an app setting |
GetConnectionString(name) |
Resolve a connection string |
EnvironmentOf(key, kind) |
The environment name a key resolves to |
InstanceName |
The instance this client resolved to |
SourceOf(key, kind) |
Published, LocalConfig, or None |
All |
Every setting the client can resolve |
Environments |
The environments the app published, with names and colors |
IsAvailable / HasLocalFallback / UpdatedUtc |
Whether the app has published, whether a fallback loaded, and when |
SettingChanged |
Raised when a value changes (background thread) |
SubscribeOnUiThread(handler) / SubscribeOn(context, handler) |
Subscribe with events marshalled to the UI thread |
Refresh() |
Re-read now |
Options
| Option | Default | |
|---|---|---|
InstanceName |
default |
Must match the app's instance |
AutoDiscoverInstance |
false |
Find the instance that names this application |
ApplicationName |
the entry assembly's name | What discovery matches on |
SnapshotPath |
derived from the instance | Explicit file to read |
WatchForChanges |
true |
Live updates |
FallBackToLocalConfig |
true |
Read own config when the app has published nothing |
FallbackEnvironment |
Development |
Environment the fallback resolves to |
Environments |
Development_ / Production_ |
Prefixes recognised in the fallback |
IncludeUnprefixedFallback |
true |
Serve plain keys from own config |
UseLocalAppConfig / UseLocalJsonFile |
true |
Which fallback sources to read |
LocalJsonFilePath |
appsettings.json beside the app |
Fallback JSON file |
LockjawClient is thread-safe and implements IDisposable — dispose it to stop watching. Use
LockjawClient.Default for the common single-instance case.
Shipping
Lockjaw is a development aid. Keep production credentials out of config files that reach end users; the usual approach is to reference it from Debug builds only:
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<PackageReference Include="Lockjaw.Client" Version="2.0.1" />
</ItemGroup>
See also
- Lockjaw.Core — the shared model, if you want to read the published snapshot yourself.
- The Lockjaw app — the tray application that reads your configuration and publishes the values. Its built-in Help (F1) documents the whole system, including the real file paths on your machine.
| 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 was computed. 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
- Lockjaw.Core (>= 2.0.2.24)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
2.0.2
- Environments are open-ended: declare any prefix (Local_, Test_, Staging_) instead of only Development_/Production_.
- EnvironmentOf returns the environment name; Environments exposes the published environment list.
- Falls back to the consuming application's own app.config / appsettings.json when nothing has been published; SourceOf reports where a value came from.
- SubscribeOnUiThread / SubscribeOn marshal SettingChanged onto a WPF or WinForms UI thread.
- Recovers from FileSystemWatcher buffer overflow so a dropped event cannot leave stale values.