-
Notifications
You must be signed in to change notification settings - Fork 0
Loading Configuration
Use the fluent IniConfigBuilder API to configure a single INI file:
using var config = IniConfigRegistry.ForFile("myapp.ini")
// Search directories – tried in order until the file is found
.AddSearchPath("/etc/myapp")
.AddSearchPath(AppContext.BaseDirectory)
// Optional: apply an admin-supplied defaults file first
.AddDefaultsFile("/etc/myapp/defaults.ini")
// Optional: apply admin-forced constants last (users cannot override these)
.AddConstantsFile("/etc/myapp/constants.ini")
// Optional: keep the file locked against external writes
.LockFile()
// Optional: automatically reload when the file changes on disk
.MonitorFile()
// Register each section with its generated implementation
.RegisterSection<IDbSettings>(new DbSettingsImpl())
.RegisterSection<IAppSettings>(new AppSettingsImpl())
// Build loads the file, fires IAfterLoad hooks, and registers in the global registry
.Build();Note:
IniConfigimplementsIDisposable. Useusingto ensure the file lock and file-system watcher are released when the application exits.
Use BuildAsync to load configuration without blocking the calling thread.
This is recommended for UI applications (WPF, Avalonia, WinForms) and ASP.NET Core
services that load configuration on startup:
using var config = await IniConfigRegistry.ForFile("myapp.ini")
.AddSearchPath(AppContext.BaseDirectory)
.RegisterSection<IDbSettings>(new DbSettingsImpl())
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.BuildAsync(cancellationToken);See Async-Support for the fire-and-forget DI pattern using InitialLoadTask.
For desktop applications the natural home for a user INI file is
%APPDATA%\<ApplicationName> on Windows (~/.config/<ApplicationName> on Linux,
~/Library/Application Support/<ApplicationName> on macOS).
Use AddAppDataPath to add that directory as a search path and write target in one call:
using var config = IniConfigRegistry.ForFile("myapp.ini")
.AddAppDataPath("MyApplication") // creates the folder if it does not exist
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();
// If the file does not exist yet it will be created in AppData on the first Save().
config.Save();When you need to read from one location (e.g. a read-only system directory) and write
to a different location, use SetWritablePath:
var targetPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"MyCompany", "MyApp", "user.ini");
using var config = IniConfigRegistry.ForFile("defaults.ini")
.AddSearchPath("/etc/myapp") // read from here
.SetWritablePath(targetPath) // write to here on first Save()
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();By default the INI file is read and written as UTF-8. Use WithEncoding when working
with legacy files that use a different encoding:
using var config = IniConfigRegistry.ForFile("legacy.ini")
.AddSearchPath(".")
.WithEncoding(Encoding.Latin1)
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();Call AutoSaveInterval to flush dirty sections to disk automatically at a regular interval:
using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(".")
.AutoSaveInterval(TimeSpan.FromSeconds(30))
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();The internal timer only writes to disk when HasPendingChanges() returns true,
so no unnecessary I/O occurs. The timer is stopped automatically when config.Dispose() is called.
Call SaveOnExit to automatically flush dirty sections when the process terminates:
using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(".")
.SaveOnExit()
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();
// config.Dispose() unregisters the ProcessExit handler automatically.Call EmptyWhenNull() to make every reference-type property across all registered sections
return an empty value (e.g. string.Empty, empty list, empty array) instead of null when no
value is present in the INI file and no explicit [DefaultValue] is set:
using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(AppContext.BaseDirectory)
.EmptyWhenNull() // applies to all sections
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.RegisterSection<IDbSettings>(new DbSettingsImpl())
.Build();To scope the behaviour to a single section use [IniSection(EmptyWhenNull = true)], or to
a single property use [IniValue(EmptyWhenNull = true)]. See Empty-When-Null for the
complete guide and precedence rules.
Use the dedicated fluent methods to control how the INI file is interpreted during
load and reload. These methods all configure an internal IniParserOptions instance
that is forwarded to the parser on every file read.
using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(".")
.WithDuplicateKeyHandling(DuplicateKeyHandling.FirstWins)
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();| Method | Behaviour |
|---|---|
.WithDuplicateKeyHandling(LastWins) |
Last definition wins (default) |
.WithDuplicateKeyHandling(FirstWins) |
First definition is kept; later duplicates ignored |
.WithDuplicateKeyHandling(ThrowError) |
Throws InvalidOperationException on any duplicate |
using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(".")
.EnableQuotedValues() // key = "value" → value (quotes stripped)
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(".")
.EnableEscapeSequences() // key = C:\\Path → C:\Path
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();Decoded sequences: \\, \n, \r, \t, \0, \", \', \a, \b, \xHH.
using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(".")
.EnableLineContinuation() // trailing \ joins the next line
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(".")
.CaseSensitiveKeys() // AppName ≠ appname
.CaseSensitiveSections() // [General] ≠ [GENERAL]
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(".")
.EnableEscapeSequences()
.EnableQuotedValues()
.EnableLineContinuation()
.CaseSensitiveKeys()
.WithDuplicateKeyHandling(DuplicateKeyHandling.ThrowError)
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();Alternatively, build an IniParserOptions object and supply it in one call:
var opts = new IniParserOptions
{
EscapeSequences = true,
QuotedValues = true,
CaseSensitiveKeys = true,
DuplicateKeyHandling = DuplicateKeyHandling.ThrowError,
};
using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(".")
.WithParserOptions(opts)
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();See Parser-Options for the complete reference, including tables showing the exact effect of each option.
When plugins need to register their own INI sections before the file is read, use
Create() instead of Build(). Create() constructs the IniConfig, registers it in
the global registry, and returns it — without reading any file. Plugins can then
call AddSection<T>() on the config, and the host calls Load() once when all sections
are registered.
See Plugin-Registrations for the full three-phase pattern and examples.
// Phase 1 — create (no I/O); config is immediately visible in IniConfigRegistry
var config = IniConfigRegistry.ForFile("myapp.ini")
.AddSearchPath(AppContext.BaseDirectory)
.RegisterSection<IHostSettings>(new HostSettingsImpl())
.Create();
// Phase 2 — plugins add their sections (no I/O)
foreach (var plugin in LoadPlugins())
plugin.PreInit(); // calls config.AddSection<IPluginSettings>(...)
// Phase 3 — single load reads all files for every section
config.Load();
// Or: await config.LoadAsync(cancellationToken);-
Plugin-Registrations —
Create()+AddSection+Load()for plugin-based apps - Loading-Life-Cycle — value resolution order
-
Reloading —
Reload()/ReloadAsync()and the singleton guarantee -
Saving —
Save()/SaveAsync()andIBeforeSave/IAfterSavehooks -
File-Locking —
LockFile() -
File-Change-Monitoring —
MonitorFile() -
Async-Support —
BuildAsync()and other async APIs -
Runtime-Only-and-Constants — constants-file protection and
IsConstant(key) -
Empty-When-Null —
EmptyWhenNull()builder method and property/section-level equivalents -
Parser-Options —
IniParserOptions— configurable duplicate-key handling, quoted values, escape sequences, line continuation, and case sensitivity