State and Storage
Plugins have access to both transient (in-memory) and persistent storage.
Transient State (PluginStateStore)
In-memory state shared across plugin components within a session.
Basic Usage
From PluginComponentBase:
csharp
// Get state (returns default if not set)
var count = GetState<int>("counter");
// Set state
SetState("counter", count + 1);
// With explicit plugin ID
var value = Context.StateStore.Get<string>(pluginId, "key");
Context.StateStore.SetForPlugin<string>(GetType(), "key", "value");State Change Events
csharp
protected override void OnInitialized()
{
Context.StateStore.StateChanged += OnStateChanged;
}
private void OnStateChanged(object? sender, PluginStateChangedEventArgs e)
{
if (e.PluginId == GetPluginId() && e.Key == "mykey")
{
// React to state change
StateHasChanged();
}
}
public void Dispose()
{
Context.StateStore.StateChanged -= OnStateChanged;
}Use Cases
- UI state (selected tabs, expanded sections)
- Temporary data being edited
- Communication between components
- Cached computed values
Persistent Storage (IPluginStorage)
Key-value storage that survives application restarts.
Accessing Storage
From PluginComponentBase:
csharp
// Storage property (may be null if not configured)
var storage = Storage;
if (storage != null)
{
await storage.SetAsync("key", value);
}Via context:
csharp
var storage = Context.StorageFactory?.CreateForPlugin(GetPluginId()!);IPluginStorage Interface
csharp
public interface IPluginStorage
{
Task<T?> GetAsync<T>(string key);
Task SetAsync<T>(string key, T value);
Task<bool> DeleteAsync(string key);
Task<bool> ExistsAsync(string key);
Task<IEnumerable<string>> GetKeysAsync(string? prefix = null);
Task ClearAsync();
}Examples
Save Settings:
csharp
public class MySettings
{
public bool DarkMode { get; set; }
public int RefreshInterval { get; set; } = 30;
}
// Save
await Storage?.SetAsync("settings", new MySettings
{
DarkMode = true,
RefreshInterval = 60
});
// Load
var settings = await Storage?.GetAsync<MySettings>("settings")
?? new MySettings();Store List of Items:
csharp
// Save
await Storage?.SetAsync("favorites", new List<string> { "item1", "item2" });
// Load
var favorites = await Storage?.GetAsync<List<string>>("favorites")
?? new List<string>();Check and Delete:
csharp
if (await Storage?.ExistsAsync("temp_data") == true)
{
await Storage.DeleteAsync("temp_data");
}List Keys with Prefix:
csharp
// Get all keys starting with "cache_"
var cacheKeys = await Storage?.GetKeysAsync("cache_") ?? [];
foreach (var key in cacheKeys)
{
await Storage.DeleteAsync(key);
}Persistent Plugin Marker
Implement IPersistentPlugin for data management features:
csharp
public class MyPluginManifest : IPluginManifest, IPersistentPlugin
{
// IPluginManifest properties...
// IPersistentPlugin
public string DataDisplayName => "My Plugin Settings";
public string ExportFileExtension => "json";
public bool PromptDeleteOnRemove => true;
}Properties:
| Property | Description |
|---|---|
DataDisplayName | Name shown in data management UI |
ExportFileExtension | File extension for exports (default: "json") |
PromptDeleteOnRemove | Ask to delete data when plugin removed |
Platform-Specific Implementations
Desktop (LiteDB)
- Uses LiteDB for file-based storage
- Data stored in application data directory
- Supports complex object graphs
WebAssembly (localStorage)
- Uses browser localStorage
- Data serialized as JSON
- Size limits apply (~5MB typically)
Best Practices
When to Use Transient State
- Temporary UI state
- Data being actively edited
- Cross-component communication within session
- Cached computations
When to Use Persistent Storage
- User preferences/settings
- Saved work that should survive restarts
- Authentication tokens
- Historical data
Key Naming Conventions
csharp
// Good: Descriptive, prefixed for organization
await Storage.SetAsync("settings.display", displaySettings);
await Storage.SetAsync("cache.users", userCache);
await Storage.SetAsync("history.searches", searchHistory);
// Avoid: Generic, collision-prone
await Storage.SetAsync("data", myData); // Too generic
await Storage.SetAsync("temp", value); // Consider transient stateSerialization Considerations
Storage serializes to JSON. Ensure types are serializable:
csharp
// Good: Simple POCO
public class Settings
{
public string Theme { get; set; } = "default";
public int FontSize { get; set; } = 14;
}
// Problematic: Circular references, complex types
public class BadSettings
{
public Stream DataStream { get; set; } // Can't serialize
public BadSettings Parent { get; set; } // Circular reference
}