CrystalData 0.47.0
See the version list below for details.
dotnet add package CrystalData --version 0.47.0
NuGet\Install-Package CrystalData -Version 0.47.0
<PackageReference Include="CrystalData" Version="0.47.0" />
<PackageVersion Include="CrystalData" Version="0.47.0" />
<PackageReference Include="CrystalData" />
paket add CrystalData --version 0.47.0
#r "nuget: CrystalData, 0.47.0"
#:package CrystalData@0.47.0
#addin nuget:?package=CrystalData&version=0.47.0
#tool nuget:?package=CrystalData&version=0.47.0
CrystalData
CrystalData is a persistence engine for .NET. It combines snapshot files, optional journals, backups, and independently loaded storage nodes with Tinyhand serialization and ValueLink collections.
Contents
- Requirements
- Installation
- Quick start
- NativeAOT
- Configuration
- Paths and backups
- Saving and shutdown
- Journaling
- Auxiliary storage
- S3 storage
- Recovery
- Samples
Requirements
- .NET 10 SDK or later
Installation
dotnet add package CrystalData
Package Manager Console:
Install-Package CrystalData
Quick start
Define a Tinyhand-serializable data type:
using System.ComponentModel;
using Tinyhand;
[TinyhandObject]
public partial class FirstData
{
[Key(0)]
public int Id { get; set; }
[Key(1)]
[DefaultValue("Hoge")]
public string Name { get; set; } = string.Empty;
}
Register the crystal, prepare the storage services, update the data, and shut down cleanly:
using CrystalData;
using Microsoft.Extensions.DependencyInjection;
var product = new CrystalUnit.Builder()
.ConfigureCrystal(context =>
{
context.AddCrystal<FirstData>(
new CrystalConfiguration(
new LocalFileConfiguration("Local/FirstData.tinyhand"))
{
SaveFormat = SaveFormat.Utf8,
NumberOfFileHistories = 1,
});
})
.Build();
var services = product.Context.ServiceProvider;
var control = services.GetRequiredService<CrystalControl>();
var result = await control.PrepareAndLoad(useQuery: false);
if (result.IsFailure())
{
throw new InvalidOperationException($"CrystalData initialization failed: {result}");
}
var data = services.GetRequiredData<FirstData>();
data.Id++;
data.Name = "Updated";
await control.StoreAndRip();
StoreAndRip is terminal: the CrystalControl instance cannot be used after it completes. Call it during application shutdown.
NativeAOT
CrystalData supports NativeAOT. Data types must use Tinyhand source generation and be registered through the generic AddCrystal<TData>, CreateCrystal<TData>, or GetOrCreateCrystal<TData> APIs so their closed generic forms are visible at build time.
Publish an application for a specific runtime identifier:
dotnet publish -c Release -r win-x64 --self-contained -p:PublishAot=true
The CrystalData project enables the .NET AOT and trimming compatibility analyzers, and CI publishes and runs QuickStart as a native executable.
Configuration
Each registered type has a CrystalConfiguration.
| Property | Purpose |
|---|---|
SaveFormat |
Selects binary or UTF-8 Tinyhand output. The default comes from CrystalOptions.DefaultSaveFormat. |
Volatile |
Keeps the crystal in memory without writing snapshot files. |
SaveInterval |
Sets the automatic snapshot interval for the crystal. |
NumberOfFileHistories |
Sets the number of retained snapshot files. Use 0 to disable file histories. |
FileConfiguration |
Selects the primary snapshot file. |
BackupFileConfiguration |
Selects an optional backup snapshot file. |
StorageConfiguration |
Configures independently loaded StoragePoint<T> data. |
RequiredForLoading |
Passes failures for previously stored data to the recovery query. |
Global defaults and limits are configured with CrystalOptions:
context.SetCrystalOptions(new CrystalOptions
{
GlobalDirectory = new LocalDirectoryConfiguration("Data"),
DefaultBackup = new LocalDirectoryConfiguration("Backup"),
DefaultSaveFormat = SaveFormat.Binary,
MemoryUsageLimit = 512L * 1024 * 1024,
});
Register more than one crystal when an application has independently persisted data sets:
context.AddCrystal<FirstData>(
new CrystalConfiguration(new GlobalFileConfiguration("First.tinyhand")));
context.AddCrystal<SecondData>(
new CrystalConfiguration(new GlobalFileConfiguration("Second.tinyhand")));
Crystals can also be created at runtime with CreateCrystal<TData> or retrieved or created with GetOrCreateCrystal<TData>.
Paths and backups
LocalFileConfigurationandLocalDirectoryConfigurationuse absolute paths as-is. Relative paths are resolved againstCrystalOptions.DataDirectory.GlobalFileConfigurationandGlobalDirectoryConfigurationare resolved relative toCrystalOptions.GlobalDirectory.EmptyFileConfigurationandEmptyDirectoryConfigurationdisable the corresponding file or directory.S3FileConfigurationandS3DirectoryConfigurationidentify objects in an S3 bucket.
Set BackupFileConfiguration for one crystal, or set CrystalOptions.DefaultBackup to derive backup locations for crystals, journals, and auxiliary storage that do not define one explicitly.
Snapshot histories provide recovery candidates when the current file is missing or invalid. Journaling requires at least one history file for every journaled crystal.
Saving and shutdown
Use the lifecycle method that matches the operation:
| Method | Behavior |
|---|---|
PrepareAndLoad() |
Prepares persistence services and loads registered crystals. |
Store() |
Persists all managed crystals and auxiliary storage. |
StoreAndRelease() |
Persists all managed data and attempts to release its resources. |
StoreAndRip() |
Persists all managed data, records a clean shutdown, and terminates services. |
For a single crystal, call ICrystal.StoreData(). For independently loaded nodes, call StoragePoint<T>.AddToSaveQueue() to schedule persistence or StoragePoint<T>.StoreData() to request it directly.
Journaling
Journaling records changes to Tinyhand structural objects between snapshots. Configure a journal and use [TinyhandObject(Structural = true)] on journaled data:
[TinyhandObject(Structural = true)]
public partial class JournalData
{
[Key(0)]
public partial int Count { get; set; }
}
var product = new CrystalUnit.Builder()
.ConfigureCrystal(context =>
{
context.SetJournal(
new SimpleJournalConfiguration(
new LocalDirectoryConfiguration("Data/Journal")));
context.AddCrystal<JournalData>(
new CrystalConfiguration(
new LocalFileConfiguration("Data/JournalData.tinyhand"))
{
NumberOfFileHistories = 3,
});
})
.Build();
Structural members must be compatible with Tinyhand's structural serialization rules. ValueLink collections can also be used as journaled roots.
Auxiliary storage
StoragePoint<T> keeps a child object in an independently loaded file. Configure auxiliary storage on the owning crystal:
context.AddCrystal<RootData>(
new CrystalConfiguration(new LocalFileConfiguration("Data/Root.tinyhand"))
{
StorageConfiguration = new SimpleStorageConfiguration(
new LocalDirectoryConfiguration("Data/Storage"))
{
NumberOfHistoryFiles = 3,
},
});
The type containing a storage point must be a Tinyhand structural object. StoragePoint<T> reserves Tinyhand key 0; derived storage-point types must start their own keys at 1.
Read with TryGet. Use TryLock whenever data will be changed, and dispose the returned DataScope<T> to release the lock:
using var scope = await root.Child.TryLock(AcquisitionMode.GetOrCreate);
if (scope.IsValid)
{
scope.Data.Count++;
}
Avoid replacing a storage-point instance with Set unless instance replacement is specifically required.
S3 storage
Supply bucket credentials through IStorageKey, then use an S3 file or directory configuration:
var storageKey = services.GetRequiredService<IStorageKey>();
storageKey.AddKey(
"my-bucket",
new AccessKeyPair("ACCESS_KEY_ID", "SECRET_ACCESS_KEY"));
var configuration = new CrystalConfiguration(
new S3FileConfiguration("my-bucket", "app/FirstData.tinyhand"));
Do not embed production credentials in source code. Provide them through the application's secret-management mechanism.
Recovery
PrepareAndLoad(useQuery: true) consults the registered ICrystalDataQuery when recovery requires a decision. Pass false for non-interactive startup behavior. The second argument, loadCrystals, can defer loading registered crystals while still preparing persistence services.
CrystalData checks the primary snapshot, available histories, and configured backups. For previously stored data with RequiredForLoading = true, a load failure is passed to ICrystalDataQuery; initialization fails when the query chooses to abort.
Samples
- QuickStart contains the smallest complete application.
- Advanced covers backups, dynamic configuration, journals, paths, dependency injection, save timing, and storage points.
License
CrystalData is licensed under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- Arc.Collections (>= 1.45.0)
- Arc.Crypto (>= 1.50.2)
- Arc.Threading (>= 0.52.0)
- Arc.Unit (>= 0.46.0)
- AWSSDK.S3 (>= 4.0.102.4)
- Microsoft.Extensions.DependencyInjection (>= 10.0.11)
- Tinyhand (>= 0.144.1)
- ValueLink (>= 0.118.2)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on CrystalData:
| Package | Downloads |
|---|---|
|
Lp
Lp is an experimental program that create value. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.48.0 | 47 | 9/7/2026 |
| 0.47.0 | 65 | 9/4/2026 |
| 0.46.2 | 71 | 9/1/2026 |
| 0.46.1 | 100 | 8/21/2026 |
| 0.46.0 | 105 | 8/20/2026 |
| 0.45.4 | 175 | 5/14/2026 |
| 0.45.3 | 165 | 5/11/2026 |
| 0.45.2 | 136 | 5/10/2026 |
| 0.45.0 | 131 | 5/8/2026 |
| 0.44.0 | 160 | 4/23/2026 |
| 0.43.1 | 130 | 4/23/2026 |
| 0.43.0 | 124 | 4/22/2026 |
| 0.42.3 | 138 | 4/16/2026 |
| 0.42.2 | 139 | 4/7/2026 |
| 0.42.1 | 128 | 4/7/2026 |
| 0.42.0 | 132 | 4/6/2026 |
| 0.41.1 | 138 | 4/6/2026 |
| 0.41.0 | 140 | 4/5/2026 |
| 0.40.0 | 131 | 4/3/2026 |
| 0.39.3 | 140 | 3/31/2026 |