ktsu.AppData
1.0.7-pre.1
Prefix Reserved
dotnet add package ktsu.AppData --version 1.0.7-pre.1
NuGet\Install-Package ktsu.AppData -Version 1.0.7-pre.1
<PackageReference Include="ktsu.AppData" Version="1.0.7-pre.1" />
<PackageVersion Include="ktsu.AppData" Version="1.0.7-pre.1" />
<PackageReference Include="ktsu.AppData" />
paket add ktsu.AppData --version 1.0.7-pre.1
#r "nuget: ktsu.AppData, 1.0.7-pre.1"
#addin nuget:?package=ktsu.AppData&version=1.0.7-pre.1&prerelease
#tool nuget:?package=ktsu.AppData&version=1.0.7-pre.1&prerelease
AppData
A modern, SOLID-compliant application data storage library for .NET that provides type-safe persistence with full dependency injection support.
โจ Features
- ๐๏ธ SOLID Architecture: Clean separation of concerns with dependency injection
- ๐ฏ Type Safety: Strongly-typed paths using
ktsu.Semantics
- ๐ง Dependency Injection: Full DI support with standard .NET patterns
- ๐งช Testable: Easy mocking and isolated unit testing
- ๐ Backup & Recovery: Automatic backup and corruption recovery
- โก Performance: Debounced saves and efficient file operations
- ๐ Thread Safe: Safe for concurrent access
- ๐ฆ Zero Dependencies: Minimal external dependencies
๐ Quick Start
Installation
dotnet add package ktsu.AppData
Basic Usage
- Configure services (Program.cs):
using ktsu.AppData.Configuration;
var services = new ServiceCollection();
services.AddAppData();
services.AddTransient<IMyService, MyService>();
using var serviceProvider = services.BuildServiceProvider();
- Create your data model:
using ktsu.AppData;
public class UserSettings : AppData<UserSettings>
{
public string Theme { get; set; } = "Light";
public string Language { get; set; } = "English";
public Dictionary<string, string> Preferences { get; set; } = new();
}
- Use in your services:
using ktsu.AppData.Interfaces;
public class MyService
{
private readonly IAppDataRepository<UserSettings> _repository;
public MyService(IAppDataRepository<UserSettings> repository)
{
_repository = repository;
}
public void SaveUserPreferences(string theme, string language)
{
var settings = new UserSettings
{
Theme = theme,
Language = language
};
settings.Save(_repository);
}
public UserSettings LoadUserPreferences()
{
return _repository.LoadOrCreate();
}
}
๐ Usage Examples
ASP.NET Core Integration
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add AppData services
builder.Services.AddAppData(options =>
{
// Custom JSON options
options.JsonSerializerOptions = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
});
// Add your services
builder.Services.AddScoped<IUserService, UserService>();
var app = builder.Build();
Custom File Locations
public class DatabaseConfig : AppData<DatabaseConfig>
{
public string ConnectionString { get; set; } = "";
public int TimeoutSeconds { get; set; } = 30;
// Save to custom subdirectory
protected override RelativeDirectoryPath? Subdirectory =>
"database".As<RelativeDirectoryPath>();
// Use custom filename
protected override FileName? FileNameOverride =>
"db_config.json".As<FileName>();
}
Queued Saves with Debouncing
public class RealTimeService
{
private readonly IAppDataRepository<AppState> _repository;
private readonly AppState _state = new();
public RealTimeService(IAppDataRepository<AppState> repository)
{
_repository = repository;
}
public void UpdateState(string key, string value)
{
_state.Data[key] = value;
_state.QueueSave(); // Queues save, doesn't write immediately
}
public async Task FlushChanges()
{
_state.SaveIfRequired(_repository); // Only saves if debounce time elapsed
}
}
๐งช Testing
The library provides excellent testing support with mock file systems:
[Test]
public async Task UserService_SavesSettings_Successfully()
{
// Arrange
var services = new ServiceCollection();
services.AddAppDataForTesting(() => new MockFileSystem());
services.AddTransient<IUserService, UserService>();
using var serviceProvider = services.BuildServiceProvider();
var userService = serviceProvider.GetRequiredService<IUserService>();
// Act
userService.SaveUserPreferences("Dark", "Spanish");
// Assert
var settings = userService.LoadUserPreferences();
Assert.AreEqual("Dark", settings.Theme);
Assert.AreEqual("Spanish", settings.Language);
}
โ๏ธ Configuration Options
Default Configuration
services.AddAppData();
Custom Serialization
services.AddAppData(options =>
{
options.JsonSerializerOptions = new JsonSerializerOptions
{
WriteIndented = false,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
});
Custom File System
services.AddAppData(options =>
{
options.FileSystemFactory = _ => new MyCustomFileSystem();
});
Replace Components
services.AddAppData();
// Replace with custom implementations
services.Replace(ServiceDescriptor.Singleton<IAppDataSerializer, XmlSerializer>());
services.Replace(ServiceDescriptor.Singleton<IAppDataPathProvider, CustomPathProvider>());
๐๏ธ Architecture
The library follows SOLID principles with a clean, dependency-injection-based architecture:
graph TD
A[AppData<T><br/>Data Model] --> B[IAppDataRepository<T><br/>Operations]
B --> C[IAppDataFileManager<br/>File Operations]
B --> D[IAppDataSerializer<br/>JSON Serialization]
B --> E[IAppDataPathProvider<br/>Path Management]
C --> E
F[Your Service] --> B
G[DI Container] --> B
G --> C
G --> D
G --> E
Core Interfaces
IAppDataRepository<T>
: High-level data operations (Load, Save)IAppDataFileManager
: File I/O with backup/recoveryIAppDataSerializer
: Data serialization (JSON by default)IAppDataPathProvider
: Type-safe path management
๐ File Storage
Data is stored in the user's application data directory:
Windows: %APPDATA%\{ApplicationName}\
macOS: ~/Library/Application Support/{ApplicationName}/
Linux: ~/.config/{ApplicationName}/
Files are saved with automatic backup and recovery:
- Primary file:
user_settings.json
- Backup file:
user_settings.json.bk
(temporary during writes) - Recovery: Automatic restoration from backup if primary file is corrupted
๐ Migration Guide
From Version 1.x (Static API)
Old (v1.x):
public class Settings : AppData<Settings>
{
public string Theme { get; set; }
}
// Static usage
var settings = Settings.Get();
settings.Theme = "Dark";
settings.Save();
New (v2.x):
public class Settings : AppData<Settings>
{
public string Theme { get; set; }
}
// Dependency injection
public class MyService
{
private readonly IAppDataRepository<Settings> _repository;
public MyService(IAppDataRepository<Settings> repository)
{
_repository = repository;
}
public void UpdateTheme(string theme)
{
var settings = _repository.LoadOrCreate();
settings.Theme = theme;
settings.Save(_repository);
}
}
๐ฏ Best Practices
1. Use Dependency Injection
Always inject IAppDataRepository<T>
rather than using static methods:
โ Good:
public MyService(IAppDataRepository<Settings> repository)
{
_repository = repository;
}
โ Avoid:
var repository = AppData.GetRepository<Settings>(); // Static access
2. Handle Disposal Properly
Save queued changes before disposal:
using var settings = new Settings();
settings.QueueSave();
settings.SaveIfRequired(repository); // Save before disposal
3. Use Custom Paths Appropriately
Override paths for logical grouping:
public class DatabaseSettings : AppData<DatabaseSettings>
{
protected override RelativeDirectoryPath? Subdirectory =>
"database".As<RelativeDirectoryPath>();
}
public class UiSettings : AppData<UiSettings>
{
protected override RelativeDirectoryPath? Subdirectory =>
"ui".As<RelativeDirectoryPath>();
}
4. Test with Mock File Systems
Always use AddAppDataForTesting()
in unit tests:
services.AddAppDataForTesting(() => new MockFileSystem());
๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
๐ License
This project is licensed under the MIT License - see the LICENSE.md file for details.
๐ Related Packages
ktsu.Semantics
- Type-safe semantic typesktsu.CaseConverter
- String case conversion utilitiesktsu.ToStringJsonConverter
- Custom JSON converters
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | 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 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. |
-
net9.0
- ktsu.CaseConverter (>= 1.3.2)
- ktsu.RoundTripStringJsonConverter (>= 1.0.2)
- ktsu.Semantics (>= 1.0.19)
- Microsoft.Extensions.DependencyInjection (>= 9.0.6)
- TestableIO.System.IO.Abstractions (>= 22.0.14)
- TestableIO.System.IO.Abstractions.Wrappers (>= 22.0.14)
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 |
---|---|---|
1.0.7-pre.1 | 109 | 6/18/2025 |
1.0.6 | 127 | 6/17/2025 |
1.0.5 | 126 | 6/17/2025 |
## v1.0.7-pre.1 (prerelease)
Changes since v1.0.6:
- Bump the ktsu group with 1 update ([@dependabot[bot]](https://github.com/dependabot[bot]))