ktsu.AppDataStorage
1.17.0
Prefix Reserved
dotnet add package ktsu.AppDataStorage --version 1.17.0
NuGet\Install-Package ktsu.AppDataStorage -Version 1.17.0
<PackageReference Include="ktsu.AppDataStorage" Version="1.17.0" />
<PackageVersion Include="ktsu.AppDataStorage" Version="1.17.0" />
<PackageReference Include="ktsu.AppDataStorage" />
paket add ktsu.AppDataStorage --version 1.17.0
#r "nuget: ktsu.AppDataStorage, 1.17.0"
#:package ktsu.AppDataStorage@1.17.0
#addin nuget:?package=ktsu.AppDataStorage&version=1.17.0
#tool nuget:?package=ktsu.AppDataStorage&version=1.17.0
ktsu.AppDataStorage
A .NET library for simple, persistent application data management with JSON serialization.
Introduction
ktsu.AppDataStorage is a .NET library designed to simplify the process of persisting application data. It stores configuration or state data as JSON files in the user's application data folder, with built-in safety mechanisms like automatic backups, debounced saves, and thread-safe operations. The library provides a singleton-like access pattern and supports custom subdirectories and file names for organizing data.
Features
- Easy-to-use API: Inherit from
AppData<T>and get automatic JSON persistence withLoadOrCreate(),Save(), andGet(). - Automatic Backup: Creates backup files before overwriting to prevent data loss, with timestamped collision handling.
- Safe Write Pattern: Writes to a temporary file first, then atomically replaces the original to avoid corruption.
- Debounced Saves:
QueueSave()andSaveIfRequired()prevent frequent file writes with a 3-second debounce window. - Thread-Safe Operations: All file operations are synchronized with lock objects (uses
Locktype on .NET 9+,objecton earlier versions). - Singleton Access:
Get()provides lazy-initialized, singleton-like access to your app data instance. - Custom Storage Locations: Support for custom subdirectories and file names via
LoadOrCreate()overloads. - File System Abstraction: Uses
System.IO.Abstractionsfor easy unit testing with mock file systems. - Corrupt File Recovery: Automatically falls back to backup files when the main data file is corrupt or missing.
- Dispose-on-Exit: Registers for process exit to ensure queued saves are flushed before the application terminates.
Installation
Package Manager Console
Install-Package ktsu.AppDataStorage
.NET CLI
dotnet add package ktsu.AppDataStorage
Package Reference
<PackageReference Include="ktsu.AppDataStorage" Version="x.y.z" />
Usage Examples
Basic Example
Create a class that inherits from AppData<T>, where T is your custom data type.
using ktsu.AppDataStorage;
public class MySettings : AppData<MySettings>
{
public string Theme { get; set; } = "light";
public int FontSize { get; set; } = 14;
public bool AutoSave { get; set; } = true;
}
// Load existing data or create a new instance
var settings = MySettings.LoadOrCreate();
Console.WriteLine(settings.Theme); // "light"
Console.WriteLine(settings.FontSize); // 14
Accessing the Singleton Instance
The Get() method provides a lazy-initialized singleton instance, automatically calling LoadOrCreate() on first access.
using ktsu.AppDataStorage;
// Access the singleton from anywhere in your application
var settings = MySettings.Get();
settings.Theme = "dark";
settings.Save();
// Same instance returned every time
var sameSettings = MySettings.Get();
Console.WriteLine(sameSettings.Theme); // "dark"
Saving Data
Modify properties and call Save() to persist changes immediately.
using ktsu.AppDataStorage;
var settings = MySettings.Get();
settings.Theme = "dark";
settings.FontSize = 16;
settings.Save();
Custom Storage Location
Use overloads of LoadOrCreate() to store data in subdirectories or with custom file names.
using ktsu.AppDataStorage;
using ktsu.Semantics.Paths;
// Store in a subdirectory
var profileData = MySettings.LoadOrCreate(RelativeDirectoryPath.Create("profiles"));
// Store with a custom file name
var customData = MySettings.LoadOrCreate(FileName.Create("user_preferences.json"));
// Both subdirectory and custom file name
var specificData = MySettings.LoadOrCreate(
RelativeDirectoryPath.Create("profiles"),
FileName.Create("admin_settings.json"));
Advanced Usage
Queued and Debounced Saving
For scenarios with frequent updates (e.g., UI-driven changes), use QueueSave() to schedule a save that is debounced with a 3-second threshold. Call SaveIfRequired() periodically (e.g., in a game loop or timer) to flush queued saves.
using ktsu.AppDataStorage;
var settings = MySettings.Get();
settings.Theme = "dark";
settings.QueueSave(); // Schedules a save
// Later, in your update loop or timer:
settings.SaveIfRequired(); // Saves only if 3+ seconds have elapsed since QueueSave
// Or use the static convenience methods:
MySettings.QueueSave();
MySettings.SaveIfRequired();
Queued saves are also automatically flushed when the AppData<T> instance is disposed or when the process exits.
Testing with Mock File Systems
The library supports System.IO.Abstractions for testability. Configure a mock file system in your tests:
using System.IO.Abstractions.TestingHelpers;
using ktsu.AppDataStorage;
// In test setup - each thread gets its own isolated instance
AppData.ConfigureForTesting(() => new MockFileSystem());
// Run your tests...
var data = MySettings.LoadOrCreate();
data.Theme = "test";
data.Save();
// In test teardown
AppData.ResetFileSystem();
Directory and File Paths
Data is stored in a directory unique to the current application domain under the user's %APPDATA% folder. File names are derived from the class name in snake_case.
using ktsu.AppDataStorage;
// View the storage path
Console.WriteLine(AppData.Path);
// e.g., C:\Users\{user}\AppData\Roaming\{AppDomainName}
// File name is automatically generated from the class name
// MySettings -> my_settings.json
API Reference
AppData Static Class
Provides static helper methods and properties for managing application data storage.
Properties
| Name | Type | Description |
|---|---|---|
Path |
AbsoluteDirectoryPath |
The path where persistent data is stored for this application |
Methods
| Name | Return Type | Description |
|---|---|---|
WriteText<T>(T appData, string text) |
void |
Writes text to an app data file using a safe write pattern |
ReadText<T>(T appData) |
string |
Reads text from an app data file, falling back to backup if missing |
QueueSave<T>(this T appData) |
void |
Extension method that queues a debounced save operation |
SaveIfRequired<T>(this T appData) |
void |
Extension method that saves if the debounce threshold has elapsed |
ConfigureForTesting(Func<IFileSystem>) |
void |
Configures a mock file system factory for unit testing |
ResetFileSystem() |
void |
Resets the file system to the default implementation after testing |
AppData<T> Generic Abstract Class
Base class for app data storage. Inherit from this class to create persistable data types.
Type Constraints
T : AppData<T>, IDisposable, new()
Instance and Static Methods
| Name | Return Type | Description |
|---|---|---|
Get() |
T |
Gets the lazy-initialized singleton instance of the app data |
LoadOrCreate() |
T |
Loads app data from file or creates a new instance if none exists |
LoadOrCreate(RelativeDirectoryPath?) |
T |
Loads or creates with a custom subdirectory |
LoadOrCreate(FileName?) |
T |
Loads or creates with a custom file name |
LoadOrCreate(RelativeDirectoryPath?, FileName?) |
T |
Loads or creates with both custom subdirectory and file name |
Save() |
void |
Serializes and saves the app data to its JSON file |
QueueSave() |
void |
Queues a debounced save for the singleton instance |
SaveIfRequired() |
void |
Saves the singleton instance if the debounce threshold has elapsed |
Dispose() |
void |
Disposes the instance, flushing any queued saves |
Contributing
Contributions are welcome! Feel free to open issues or submit pull requests.
License
This project is licensed under the MIT License. See the LICENSE.md file for details.
| 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 is compatible. 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 is compatible. 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 is compatible. 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 is compatible. |
| .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 |