Lockjaw.Client 2.0.2.17

There is a newer version of this package available.
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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Lockjaw.Client" Version="2.0.2.17" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Lockjaw.Client" Version="2.0.2.17" />
                    
Directory.Packages.props
<PackageReference Include="Lockjaw.Client" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Lockjaw.Client --version 2.0.2.17
                    
#r "nuget: Lockjaw.Client, 2.0.2.17"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Lockjaw.Client@2.0.2.17
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Lockjaw.Client&version=2.0.2.17
                    
Install as a Cake Addin
#tool nuget:?package=Lockjaw.Client&version=2.0.2.17
                    
Install as a Cake Tool

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, or None.
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 Lockjaw section. Which environments exist is the app's business. The client needs the list only when its own keys use prefixes beyond Development_ / 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 to Development, 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
2.0.2.51 83 8/14/2026
2.0.2.49 81 8/14/2026
2.0.2.37 90 8/6/2026
2.0.2.34 100 8/5/2026
2.0.2.33 86 8/5/2026
2.0.2.32 74 8/5/2026
2.0.2.28 89 8/5/2026
2.0.2.27 89 8/5/2026
2.0.2.17 106 7/25/2026

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.