Akavache is an asynchronous, persistent (i.e., writes to disk) key-value store created for writing desktop and mobile applications in C#, based on SQLite3. Akavache is great for both storing important data (i.e., user settings) as well as cached local data that expires.
Akavache V11.0 introduces a new Builder Pattern for initialization, improved serialization support, and enhanced cross-serializer compatibility:
Akavache V11.0 represents a significant evolution in the library's architecture, developed through extensive testing and community feedback in our incubator project. The new features and improvements in V11.0 were first prototyped and battle-tested in the ReactiveMarbles.CacheDatabase repository, which served as an experimental ground for exploring new caching concepts and architectural patterns.
Key Development Milestones:
This careful incubation process ensured that V11.0 delivers not just new features, but a more robust, flexible, and maintainable caching solution that builds upon years of community experience and testing. The ReactiveMarbles organization continues to serve as a proving ground for innovative reactive programming concepts that eventually make their way into the broader ReactiveUI ecosystem.
<PackageReference Include="Akavache.Sqlite3" Version="11.0.*" /> <PackageReference Include="Akavache.SystemTextJson" Version="11.0.*" />
using Akavache.Core; using Akavache.SystemTextJson; using Akavache.Sqlite3; // Initialize with the builder pattern CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) .WithSqliteDefaults());
// Store an object var user = new User { Name = "John", Email = "john@example.com" }; await CacheDatabase.UserAccount.InsertObject("current_user", user); // Retrieve an object var cachedUser = await CacheDatabase.UserAccount.GetObject<User>("current_user"); // Store with expiration await CacheDatabase.LocalMachine.InsertObject("temp_data", someData, DateTimeOffset.Now.AddHours(1)); // Get or fetch pattern var data = await CacheDatabase.LocalMachine.GetOrFetchObject("api_data", async () => await httpClient.GetFromJsonAsync<ApiResponse>("https://api.example.com/data"));
Akavache V11.0 uses a modular package structure. Choose the packages that match your needs:
Core Package (Included with Serializers, In Memory only)<PackageReference Include="Akavache" Version="11.0.**" />Sqlite Storage Backends (recommended)
<!-- SQLite persistence --> <PackageReference Include="Akavache.Sqlite3" Version="11.0.**" /> <!-- Encrypted SQLite persistence --> <PackageReference Include="Akavache.EncryptedSqlite3" Version="11.0.**" />Serializers (Choose One (Required!))
<!-- System.Text.Json (fastest, .NET native) --> <PackageReference Include="Akavache.SystemTextJson" Version="11.0.**" /> <!-- Newtonsoft.Json (most compatible) --> <PackageReference Include="Akavache.NewtonsoftJson" Version="11.0.**" />
<!-- Image/Bitmap support --> <PackageReference Include="Akavache.Drawing" Version="11.0.**" /> <!-- Settings helpers --> <PackageReference Include="Akavache.Settings" Version="11.0.**" />
BlobCache.ApplicationName
and Registrations.Start()
methods are replaced with the builder pattern// V10.x initialization BlobCache.ApplicationName = "MyApp"; // or Akavache.Registrations.Start("MyApp"); // Usage var data = await BlobCache.UserAccount.GetObject<MyData>("key"); await BlobCache.LocalMachine.InsertObject("key", myData);
// V11.0 initialization CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) // Required! .WithSqliteDefaults()); // Usage (same API) var data = await CacheDatabase.UserAccount.GetObject<MyData>("key"); await CacheDatabase.LocalMachine.InsertObject("key", myData);
Create this helper method to ease migration:
public static class AkavacheMigration { public static void InitializeV11(string appName) { // Initialize with SQLite (most common V10.x setup) CacheDatabase.Initialize(builder => builder.WithApplicationName(appName) .WithSerializer(new SystemJsonSerializer()) // Choose your preferred serializer, Required! .WithSqliteDefaults()); } } // Then in your app: AkavacheMigration.InitializeV11("MyApp");
Akavache V11.0 uses a fluent builder pattern for configuration:
CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") // Required .WithSerializer(new SystemJsonSerializer()) // Custom serializer .WithSqliteDefaults()); // SQLite persistence1. In-Memory Only (for testing or non retensive applications)
CacheDatabase.Initialize(builder => builder.WithApplicationName("TestApp") .WithSerializer(new SystemJsonSerializer()) // Custom serializer .WithInMemoryDefaults());
CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) // Custom serializer .WithSqliteDefaults());
CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) // Custom serializer .WithSqliteDefaults("mySecretPassword"));4. Custom Cache Instances
CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) // Custom serializer .WithUserAccount(new SqliteBlobCache("custom-user.db")) .WithLocalMachine(new SqliteBlobCache("custom-local.db")) .WithSecure(new EncryptedSqliteBlobCache("secure.db", "password")) .WithInMemory(new InMemoryBlobCache()));
// Set global DateTime behavior CacheDatabase.ForcedDateTimeKind = DateTimeKind.Utc; CacheDatabase.Serializer = new SystemJsonSerializer(); CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp").WithSqliteDefaults());
Akavache V11.0 supports multiple serialization formats with automatic cross-compatibility.
System.Text.Json (Recommended)Best for: New applications, performance-critical scenarios, .NET native support
CacheDatabase.Serializer = new SystemJsonSerializer();
Features:
Configuration:
var serializer = new SystemJsonSerializer() { UseBsonFormat = false, // true for max compatibility with old data Options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false } }; CacheDatabase.Serializer = serializer;Newtonsoft.Json (Maximum Compatibility)
Best for: Migrating from older Akavache versions, complex serialization needs
CacheDatabase.Serializer = new NewtonsoftSerializer();
Features:
Configuration:
var serializer = new NewtonsoftSerializer() { UseBsonFormat = true, // Recommended for Akavache compatibility Options = new JsonSerializerSettings { DateTimeZoneHandling = DateTimeZoneHandling.Utc, NullValueHandling = NullValueHandling.Ignore } }; CacheDatabase.Serializer = serializer;
For maximum compatibility with existing Akavache data:
// System.Text.Json with BSON support CacheDatabase.Serializer = new SystemJsonBsonSerializer(); // Newtonsoft.Json with BSON support CacheDatabase.Serializer = new NewtonsoftBsonSerializer();Cross-Serializer Compatibility
V11.0 can automatically read data written by different serializers:
// Data written with Newtonsoft.Json BSON can be read by System.Text.Json // Data written with System.Text.Json can be read by Newtonsoft.Json // Automatic format detection handles the conversion
Akavache provides four types of caches, each with different characteristics:
Purpose: User settings and preferences that should persist and potentially sync across devices.
// Store user preferences var settings = new UserSettings { Theme = "Dark", Language = "en-US" }; await CacheDatabase.UserAccount.InsertObject("user_settings", settings); // Retrieve preferences var userSettings = await CacheDatabase.UserAccount.GetObject<UserSettings>("user_settings");
Platform Behavior:
Purpose: Cached data that can be safely deleted by the system.
// Cache API responses var apiData = await httpClient.GetFromJsonAsync<ApiResponse>("https://api.example.com/data"); await CacheDatabase.LocalMachine.InsertObject("api_cache", apiData, DateTimeOffset.Now.AddHours(6)); // Retrieve with fallback var cachedData = await CacheDatabase.LocalMachine.GetOrFetchObject("api_cache", () => httpClient.GetFromJsonAsync<ApiResponse>("https://api.example.com/data"));
Platform Behavior:
Purpose: Encrypted storage for sensitive data like credentials and API keys.
// Store credentials await CacheDatabase.Secure.SaveLogin("john.doe", "secretPassword", "myapp.com"); // Retrieve credentials var loginInfo = await CacheDatabase.Secure.GetLogin("myapp.com"); Console.WriteLine($"User: {loginInfo.UserName}, Password: {loginInfo.Password}"); // Store API keys await CacheDatabase.Secure.InsertObject("api_key", "sk-1234567890abcdef"); var apiKey = await CacheDatabase.Secure.GetObject<string>("api_key");
Purpose: Temporary storage that doesn't persist between app sessions.
// Cache session data var sessionData = new SessionInfo { UserId = 123, SessionToken = "abc123" }; await CacheDatabase.InMemory.InsertObject("current_session", sessionData); // Fast temporary storage await CacheDatabase.InMemory.InsertObject("temp_calculation", expensiveResult);
// Store simple objects await CacheDatabase.UserAccount.InsertObject("key", myObject); // Store with expiration await CacheDatabase.LocalMachine.InsertObject("temp_key", data, DateTimeOffset.Now.AddMinutes(30)); // Store multiple objects var keyValuePairs = new Dictionary<string, MyData> { ["key1"] = new MyData { Value = 1 }, ["key2"] = new MyData { Value = 2 } }; await CacheDatabase.UserAccount.InsertObjects(keyValuePairs); // Store raw bytes await CacheDatabase.LocalMachine.Insert("raw_key", Encoding.UTF8.GetBytes("Hello World"));
// Get single object var data = await CacheDatabase.UserAccount.GetObject<MyData>("key"); // Get multiple objects var keys = new[] { "key1", "key2", "key3" }; var results = await CacheDatabase.UserAccount.GetObjects<MyData>(keys).ToList(); // Get all objects of a type var allData = await CacheDatabase.UserAccount.GetAllObjects<MyData>().ToList(); // Get raw bytes var rawData = await CacheDatabase.LocalMachine.Get("raw_key");
// Handle missing keys try { var data = await CacheDatabase.UserAccount.GetObject<MyData>("nonexistent_key"); } catch (KeyNotFoundException) { // Key not found var defaultData = new MyData(); } // Use fallback pattern var data = await CacheDatabase.UserAccount.GetObject<MyData>("key") .Catch(Observable.Return(new MyData()));
// Remove single object await CacheDatabase.UserAccount.InvalidateObject<MyData>("key"); // Remove multiple objects await CacheDatabase.UserAccount.InvalidateObjects<MyData>(new[] { "key1", "key2" }); // Remove all objects of a type await CacheDatabase.UserAccount.InvalidateAllObjects<MyData>(); // Remove all data await CacheDatabase.UserAccount.InvalidateAll();
The most common pattern for caching remote data:
// Basic get-or-fetch var userData = await CacheDatabase.LocalMachine.GetOrFetchObject("user_profile", async () => await apiClient.GetUserProfile(userId)); // With expiration var weatherData = await CacheDatabase.LocalMachine.GetOrFetchObject("weather", async () => await weatherApi.GetCurrentWeather(), DateTimeOffset.Now.AddMinutes(30)); // With custom fetch observable var liveData = await CacheDatabase.LocalMachine.GetOrFetchObject("live_data", () => Observable.Interval(TimeSpan.FromSeconds(5)) .Select(_ => DateTime.Now.ToString()));
Returns cached data immediately, then fetches fresh data:
// Subscribe to get both cached and fresh data CacheDatabase.LocalMachine.GetAndFetchLatest("news_feed", () => newsApi.GetLatestNews()) .Subscribe(news => { // This will be called twice: // 1. Immediately with cached data (if available) // 2. When fresh data arrives from the API UpdateUI(news); });
// Download and cache URLs var imageData = await CacheDatabase.LocalMachine.DownloadUrl("https://example.com/image.jpg"); // With custom headers var headers = new Dictionary<string, string> { ["Authorization"] = "Bearer " + token, ["User-Agent"] = "MyApp/1.0" }; var apiResponse = await CacheDatabase.LocalMachine.DownloadUrl("https://api.example.com/data", HttpMethod.Get, headers); // Force fresh download var freshData = await CacheDatabase.LocalMachine.DownloadUrl("https://api.example.com/live", fetchAlways: true);Login/Credential Management
// Save login credentials (encrypted) await CacheDatabase.Secure.SaveLogin("username", "password", "myapp.com"); // Retrieve credentials var loginInfo = await CacheDatabase.Secure.GetLogin("myapp.com"); Console.WriteLine($"User: {loginInfo.UserName}"); // Multiple hosts await CacheDatabase.Secure.SaveLogin("user1", "pass1", "api.service1.com"); await CacheDatabase.Secure.SaveLogin("user2", "pass2", "api.service2.com"); // Remove credentials await CacheDatabase.Secure.EraseLogin("myapp.com");
// Cache for relative time periods await CacheDatabase.LocalMachine.InsertObject("data", myData, TimeSpan.FromMinutes(30).FromNow()); // Use in get-or-fetch var cachedData = await CacheDatabase.LocalMachine.GetOrFetchObject("api_data", () => FetchFromApi(), 1.Hours().FromNow());
// Use custom scheduler for background operations CacheDatabase.TaskpoolScheduler = TaskPoolScheduler.Default; // Or use a custom scheduler CacheDatabase.TaskpoolScheduler = new EventLoopScheduler();
// Get all keys (for debugging) var allKeys = await CacheDatabase.UserAccount.GetAllKeys().ToList(); // Check when item was created var createdAt = await CacheDatabase.UserAccount.GetCreatedAt("my_key"); if (createdAt.HasValue) { Console.WriteLine($"Item created at: {createdAt.Value}"); } // Get creation times for multiple keys var creationTimes = await CacheDatabase.UserAccount.GetCreatedAt(new[] { "key1", "key2" }) .ToList();
// Force flush all pending operations await CacheDatabase.UserAccount.Flush(); // Vacuum database (SQLite only - removes deleted data) await CacheDatabase.UserAccount.Vacuum(); // Flush specific object type await CacheDatabase.UserAccount.Flush(typeof(MyDataType));
// Store different types with one operation var mixedData = new Dictionary<string, object> { ["string_data"] = "Hello World", ["number_data"] = 42, ["object_data"] = new MyClass { Value = "test" }, ["date_data"] = DateTime.Now }; await CacheDatabase.UserAccount.InsertObjects(mixedData);
Akavache.Drawing provides comprehensive image caching and bitmap manipulation functionality for Akavache applications. Built on Splat, it offers cross-platform support for loading, caching, and manipulating images with enhanced features beyond basic blob storage.
<PackageReference Include="Akavache.Drawing" Version="11.0.1" />
Akavache.Drawing requires:
Akavache.Core
- Core caching functionalitySplat.Drawing
- Cross-platform bitmap abstractionsusing Akavache.Core; using Akavache.Drawing; using Akavache.SystemTextJson; using Splat; // Initialize Akavache with drawing support CacheDatabase.Initialize(builder => builder.WithApplicationName("MyImageApp") .WithSerializer(new SystemJsonSerializer()) .WithSqliteDefaults()); // Register platform-specific bitmap loader using Splat (if needed (Net 8.0+)) Locator.CurrentMutable.RegisterPlatformBitmapLoader();2. Load Images from Cache
// Load image from cache var image = await CacheDatabase.LocalMachine.LoadImage("user_avatar"); // Load with custom sizing var thumbnail = await CacheDatabase.LocalMachine.LoadImage("user_avatar", 150, 150); // Load with error handling try { var profileImage = await CacheDatabase.UserAccount.LoadImage("profile_pic"); DisplayImage(profileImage); } catch (KeyNotFoundException) { // Image not found in cache ShowDefaultImage(); }
// Download and cache image from URL var imageFromUrl = await CacheDatabase.LocalMachine .LoadImageFromUrl("https://example.com/images/photo.jpg"); // With custom expiration var tempImage = await CacheDatabase.LocalMachine .LoadImageFromUrl("https://api.example.com/temp-image.png", absoluteExpiration: DateTimeOffset.Now.AddHours(1)); // Force fresh download (bypass cache) var freshImage = await CacheDatabase.LocalMachine .LoadImageFromUrl("https://api.example.com/live-feed.jpg", fetchAlways: true); // With custom key var namedImage = await CacheDatabase.LocalMachine .LoadImageFromUrl("user_background", "https://example.com/bg.jpg");
// Save image to cache await CacheDatabase.LocalMachine.SaveImage("user_photo", bitmap); // Save with expiration await CacheDatabase.LocalMachine.SaveImage("temp_image", bitmap, DateTimeOffset.Now.AddDays(7)); // Convert bitmap to bytes for manual storage var imageBytes = await bitmap.ImageToBytes().FirstAsync(); await CacheDatabase.LocalMachine.Insert("raw_image_data", imageBytes);
// Load multiple images at once var imageKeys = new[] { "image1", "image2", "image3" }; var loadedImages = await CacheDatabase.LocalMachine .LoadImages(imageKeys, desiredWidth: 200, desiredHeight: 200) .ToList(); foreach (var kvp in loadedImages) { Console.WriteLine($"Loaded {kvp.Key}: {kvp.Value.Width}x{kvp.Value.Height}"); } // Preload images from URLs (background caching) var urls = new[] { "https://example.com/image1.jpg", "https://example.com/image2.jpg", "https://example.com/image3.jpg" }; await CacheDatabase.LocalMachine.PreloadImagesFromUrls(urls, DateTimeOffset.Now.AddDays(1));
// Load image with automatic fallback var defaultImageBytes = File.ReadAllBytes("default-avatar.png"); var userAvatar = await CacheDatabase.UserAccount .LoadImageWithFallback("user_avatar", defaultImageBytes, 100, 100); // Load from URL with fallback var profileImage = await CacheDatabase.LocalMachine .LoadImageFromUrlWithFallback("https://example.com/profile.jpg", defaultImageBytes, desiredWidth: 200, desiredHeight: 200);
// Create and cache thumbnail from existing image await CacheDatabase.LocalMachine.CreateAndCacheThumbnail( sourceKey: "original_photo", thumbnailKey: "photo_thumb", thumbnailWidth: 150, thumbnailHeight: 150, absoluteExpiration: DateTimeOffset.Now.AddDays(30)); // Load the cached thumbnail var thumbnail = await CacheDatabase.LocalMachine.LoadImage("photo_thumb");
// Get image dimensions without fully loading var imageSize = await CacheDatabase.LocalMachine.GetImageSize("large_image"); Console.WriteLine($"Image size: {imageSize.Width}x{imageSize.Height}"); Console.WriteLine($"Aspect ratio: {imageSize.AspectRatio:F2}"); // Use size info for layout decisions if (imageSize.AspectRatio > 1.5) { // Wide image SetWideImageLayout(); } else { // Square or tall image SetNormalImageLayout(); }
// Clear images matching a pattern await CacheDatabase.LocalMachine.ClearImageCache(key => key.StartsWith("temp_")); // Clear all user avatars await CacheDatabase.UserAccount.ClearImageCache(key => key.Contains("avatar")); // Clear expired images await CacheDatabase.LocalMachine.ClearImageCache(key => key.StartsWith("cache_") && IsExpired(key));Complete Example: Photo Gallery App
public class PhotoGalleryService { private readonly IBlobCache _imageCache; private readonly IBlobCache _thumbnailCache; public PhotoGalleryService() { // Initialize Akavache with drawing support CacheDatabase.Initialize(builder => builder.WithApplicationName("PhotoGallery") .WithSerializer(new SystemJsonSerializer()) .WithSqliteDefaults()); _imageCache = CacheDatabase.LocalMachine; _thumbnailCache = CacheDatabase.UserAccount; } public async Task<IBitmap> LoadPhotoAsync(string photoId, bool generateThumbnail = false) { try { // Try to load from cache first var photo = await _imageCache.LoadImage($"photo_{photoId}"); // Generate thumbnail if requested and not exists if (generateThumbnail) { await _thumbnailCache.CreateAndCacheThumbnail( $"photo_{photoId}", $"thumb_{photoId}", 200, 200, DateTimeOffset.Now.AddMonths(1)); } return photo; } catch (KeyNotFoundException) { // Load from remote URL if not cached var photoUrl = $"https://api.photos.com/images/{photoId}"; return await _imageCache.LoadImageFromUrl($"photo_{photoId}", photoUrl, absoluteExpiration: DateTimeOffset.Now.AddDays(7)); } } public async Task<IBitmap> LoadThumbnailAsync(string photoId) { try { return await _thumbnailCache.LoadImage($"thumb_{photoId}", 200, 200); } catch (KeyNotFoundException) { // Generate thumbnail from full image var fullImage = await LoadPhotoAsync(photoId); await _thumbnailCache.SaveImage($"thumb_{photoId}", fullImage, DateTimeOffset.Now.AddMonths(1)); return await _thumbnailCache.LoadImage($"thumb_{photoId}", 200, 200); } } public async Task PreloadGalleryAsync(IEnumerable<string> photoIds) { var photoUrls = photoIds.Select(id => $"https://api.photos.com/images/{id}"); await _imageCache.PreloadImagesFromUrls(photoUrls, DateTimeOffset.Now.AddDays(7)); } public async Task ClearOldCacheAsync() { // Clear images older than 30 days await _imageCache.ClearImageCache(key => key.StartsWith("photo_") && IsOlderThan30Days(key)); // Clear thumbnails older than 60 days await _thumbnailCache.ClearImageCache(key => key.StartsWith("thumb_") && IsOlderThan60Days(key)); } private static bool IsOlderThan30Days(string key) => /* Implementation to check cache age */ false; private static bool IsOlderThan60Days(string key) => /* Implementation to check cache age */ false; }
Akavache.Settings provides a specialized settings database for installable applications. It creates persistent settings that are stored one level down from the application folder, making application updates less painful as the settings survive reinstalls.
<PackageReference Include="Akavache.Settings" Version="11.0.1" />1. Create a Settings Class
using Akavache.Settings; public class AppSettings : SettingsBase { public AppSettings() : base(nameof(AppSettings)) { } // Boolean setting with default value public bool EnableNotifications { get => GetOrCreate(true); set => SetOrCreate(value); } // String setting with default value public string UserName { get => GetOrCreate("DefaultUser"); set => SetOrCreate(value); } // Numeric settings public int MaxRetries { get => GetOrCreate(3); set => SetOrCreate(value); } public double CacheTimeout { get => GetOrCreate(30.0); set => SetOrCreate(value); } // Enum setting public LogLevel LoggingLevel { get => GetOrCreate(LogLevel.Information); set => SetOrCreate(value); } } public enum LogLevel { Debug, Information, Warning, Error }2. Initialize Settings Store
using Akavache.Core; using Akavache.SystemTextJson; using Akavache.Settings; // Initialize Akavache with settings support var appSettings = default(AppSettings); CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) .WithSettingsStore<AppSettings>(settings => appSettings = settings)); // Now use the settings appSettings.EnableNotifications = false; appSettings.UserName = "John Doe"; appSettings.MaxRetries = 5; Console.WriteLine($"User: {appSettings.UserName}"); Console.WriteLine($"Notifications: {appSettings.EnableNotifications}");Custom Settings Cache Path
By default, settings are stored in a subfolder of your application directory. You can customize this path:
CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) .WithSettingsCachePath(@"C:\MyApp\Settings") // Custom path .WithSettingsStore<AppSettings>(settings => appSettings = settings));Multiple Settings Classes
You can create multiple settings classes for different categories:
public class UserSettings : SettingsBase { public UserSettings() : base(nameof(UserSettings)) { } public string Theme { get => GetOrCreate("Light"); set => SetOrCreate(value); } } public class NetworkSettings : SettingsBase { public NetworkSettings() : base(nameof(NetworkSettings)) { } public int TimeoutSeconds { get => GetOrCreate(30); set => SetOrCreate(value); } } // Initialize multiple settings var userSettings = default(UserSettings); var networkSettings = default(NetworkSettings); CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) .WithSettingsStore<UserSettings>(settings => userSettings = settings) .WithSettingsStore<NetworkSettings>(settings => networkSettings = settings));
For sensitive settings, use encrypted storage:
public class SecureSettings : SettingsBase { public SecureSettings() : base(nameof(SecureSettings)) { } public string ApiKey { get => GetOrCreate(string.Empty); set => SetOrCreate(value); } public string DatabasePassword { get => GetOrCreate(string.Empty); set => SetOrCreate(value); } } // Initialize with encryption var secureSettings = default(SecureSettings); CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) .WithSecureSettingsStore<SecureSettings>("mySecurePassword", settings => secureSettings = settings)); // Use encrypted settings secureSettings.ApiKey = "sk-1234567890abcdef"; secureSettings.DatabasePassword = "super-secret-password";
You can specify custom database names for settings:
var appSettings = default(AppSettings); CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) .WithSettingsStore<AppSettings>( settings => appSettings = settings, "CustomAppConfig")); // Custom database name
Here's a comprehensive example showing all data types and features:
public class ComprehensiveSettings : SettingsBase { public ComprehensiveSettings() : base(nameof(ComprehensiveSettings)) { } // Basic types with defaults public bool BoolSetting { get => GetOrCreate(true); set => SetOrCreate(value); } public byte ByteSetting { get => GetOrCreate((byte)123); set => SetOrCreate(value); } public short ShortSetting { get => GetOrCreate((short)16); set => SetOrCreate(value); } public int IntSetting { get => GetOrCreate(42); set => SetOrCreate(value); } public long LongSetting { get => GetOrCreate(123456L); set => SetOrCreate(value); } public float FloatSetting { get => GetOrCreate(2.5f); set => SetOrCreate(value); } public double DoubleSetting { get => GetOrCreate(3.14159); set => SetOrCreate(value); } public string StringSetting { get => GetOrCreate("Default Value"); set => SetOrCreate(value); } // Nullable types public string? NullableStringSetting { get => GetOrCreate<string?>(null); set => SetOrCreate(value); } // Complex types (automatically serialized) public List<string> StringListSetting { get => GetOrCreate(new List<string> { "Item1", "Item2" }); set => SetOrCreate(value); } public Dictionary<string, int> DictionarySetting { get => GetOrCreate(new Dictionary<string, int> { ["Key1"] = 1, ["Key2"] = 2 }); set => SetOrCreate(value); } // Custom objects public WindowPosition WindowPosition { get => GetOrCreate(new WindowPosition { X = 100, Y = 100, Width = 800, Height = 600 }); set => SetOrCreate(value); } } public class WindowPosition { public int X { get; set; } public int Y { get; set; } public int Width { get; set; } public int Height { get; set; } } // Usage var settings = default(ComprehensiveSettings); CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSerializer(new SystemJsonSerializer()) .WithSettingsStore<ComprehensiveSettings>(s => settings = s)); // Use the settings settings.StringListSetting.Add("Item3"); settings.WindowPosition = new WindowPosition { X = 200, Y = 150, Width = 1024, Height = 768 }; settings.DictionarySetting["NewKey"] = 999;Settings Lifecycle Management Cleanup on Application Exit
// In your application shutdown code public async Task OnApplicationExit() { var builder = CacheDatabase.Builder; // Dispose settings stores to ensure data is flushed await builder.DisposeSettingsStore<AppSettings>(); await builder.DisposeSettingsStore<UserSettings>(); // Regular Akavache shutdown await CacheDatabase.Shutdown(); }Delete Settings (Reset to Defaults)
// Delete a specific settings store var builder = CacheDatabase.Builder; await builder.DeleteSettingsStore<AppSettings>(); // Settings will be recreated with default values on next access
var builder = CacheDatabase.Builder; var existingSettings = builder.GetSettingsStore<AppSettings>(); if (existingSettings != null) { Console.WriteLine("Settings already exist"); } else { Console.WriteLine("First run - settings will be created with defaults"); }
// In MauiProgram.cs public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder.UseMauiApp<App>(); // Initialize Akavache early CacheDatabase.Serializer = new SystemJsonSerializer(); CacheDatabase.ForcedDateTimeKind = DateTimeKind.Utc; CacheDatabase.Initialize(cacheBuilder => cacheBuilder.WithApplicationName("MyMauiApp") .WithSqliteDefaults()); return builder.Build(); } }
// In App.xaml.cs public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { ConfigureAkavache(); base.OnStartup(e); } protected override void OnExit(ExitEventArgs e) { // Important: Shutdown Akavache properly CacheDatabase.Shutdown().Wait(); base.OnExit(e); } private static void ConfigureAkavache() { CacheDatabase.Serializer = new SystemJsonSerializer(); CacheDatabase.ForcedDateTimeKind = DateTimeKind.Utc; CacheDatabase.Initialize(builder => builder.WithApplicationName("MyWpfApp") .WithSqliteDefaults()); } }
// In AppDelegate.cs or SceneDelegate.cs public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { CacheDatabase.Serializer = new SystemJsonSerializer(); CacheDatabase.Initialize(builder => builder.WithApplicationName("MyiOSApp") .WithSqliteDefaults()); return base.FinishedLaunching(application, launchOptions); }
// In MainActivity.cs or Application class protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); CacheDatabase.Serializer = new SystemJsonSerializer(); CacheDatabase.Initialize(builder => builder.WithApplicationName("MyAndroidApp") .WithSqliteDefaults()); }
// In App.xaml.cs protected override void OnLaunched(LaunchActivatedEventArgs e) { CacheDatabase.Serializer = new SystemJsonSerializer(); CacheDatabase.Initialize(builder => builder.WithApplicationName("MyUwpApp") .WithSqliteDefaults()); // Rest of initialization... }
Important for UWP: Mark your application as x86
or ARM
, not Any CPU
.
Performance comparison of different serializers (operations per second):
Operation System.Text.Json Newtonsoft.Json BSON Serialize small object 50,000 25,000 20,000 Deserialize small object 45,000 22,000 18,000 Serialize large object 5,000 2,500 2,000 Deserialize large object 4,500 2,200 1,800For comprehensive performance analysis and V10 vs V11 comparison:
// 1. Use System.Text.Json for best performance CacheDatabase.Serializer = new SystemJsonSerializer(); // 2. Use batch operations for multiple items await CacheDatabase.UserAccount.InsertObjects(manyItems); // 3. Set appropriate expiration times await CacheDatabase.LocalMachine.InsertObject("temp", data, 30.Minutes().FromNow()); // 4. Use InMemory cache for frequently accessed data await CacheDatabase.InMemory.InsertObject("hot_data", frequentData); // 5. Avoid storing very large objects // Instead, break them into smaller chunks or use compression // 6. Use specific types instead of object when possible await CacheDatabase.UserAccount.GetObject<SpecificType>("key"); // Good await CacheDatabase.UserAccount.Get("key", typeof(SpecificType)); // Slower
// โ Do: Initialize once at app startup public class App { static App() { CacheDatabase.Serializer = new SystemJsonSerializer(); CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp") .WithSqliteDefaults()); } } // โ Don't: Initialize multiple times
// โ Do: Use consistent, descriptive key naming await CacheDatabase.UserAccount.InsertObject("user_profile_123", userProfile); await CacheDatabase.LocalMachine.InsertObject("api_cache_weather_seattle", weatherData); // โ Do: Use constants for keys public static class CacheKeys { public const string UserProfile = "user_profile"; public const string WeatherData = "weather_data"; } // โ Don't: Use random or inconsistent keys await CacheDatabase.UserAccount.InsertObject("xyz123", someData);
// โ Do: Handle KeyNotFoundException appropriately try { var data = await CacheDatabase.UserAccount.GetObject<MyData>("key"); } catch (KeyNotFoundException) { // Provide fallback or default behavior var defaultData = new MyData(); } // โ Do: Use GetOrFetchObject for remote data var data = await CacheDatabase.LocalMachine.GetOrFetchObject("api_data", () => httpClient.GetFromJsonAsync<ApiData>("https://api.example.com/data"));
// โ Do: Use appropriate cache types await CacheDatabase.UserAccount.InsertObject("user_settings", settings); // Persistent user data await CacheDatabase.LocalMachine.InsertObject("api_cache", apiData); // Cacheable data await CacheDatabase.Secure.InsertObject("api_key", apiKey); // Sensitive data await CacheDatabase.InMemory.InsertObject("session_data", sessionData); // Temporary data
// โ Do: Set appropriate expiration times await CacheDatabase.LocalMachine.InsertObject("api_data", data, 1.Hours().FromNow()); await CacheDatabase.LocalMachine.InsertObject("image_cache", imageBytes, 1.Days().FromNow()); // โ Do: Don't expire user settings (unless necessary) await CacheDatabase.UserAccount.InsertObject("user_preferences", prefs); // No expiration
// โ Do: Always shutdown Akavache properly public override void OnExit(ExitEventArgs e) { CacheDatabase.Shutdown().Wait(); base.OnExit(e); } // For MAUI/Xamarin apps protected override void OnSleep() { CacheDatabase.Shutdown().Wait(); base.OnSleep(); }
// โ Do: Use in-memory cache for unit tests [SetUp] public void Setup() { CacheDatabase.Serializer = new SystemJsonSerializer(); CacheDatabase.Initialize(builder => builder.WithApplicationName("TestApp") .WithInMemoryDefaults()); } [TearDown] public void TearDown() { CacheDatabase.Shutdown().Wait(); }1. "No serializer has been registered"
// Fix: Register a serializer before initializing CacheDatabase.Serializer = new SystemJsonSerializer(); CacheDatabase.Initialize(/* ... */);2. "BlobCache has not been initialized"
// Fix: Call Initialize before using cache CacheDatabase.Initialize(builder => builder.WithApplicationName("MyApp").WithInMemoryDefaults()); var data = await CacheDatabase.UserAccount.GetObject<MyData>("key");3. Data compatibility issues
// Fix: Use cross-compatible serializer or migration CacheDatabase.Serializer = new NewtonsoftBsonSerializer(); // Most compatible4. SQLite errors on mobile
// Fix: Ensure SQLitePCL.raw bundle is installed // Add to your project: // <PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" Version="2.1.11" /> // If using Encrypted SQLite, also add: // <PackageReference Include="SQLitePCLRaw.lib.e_sqlcipher" Version="2.1.11" />
// Add LinkerPreserve.cs to your iOS project: public static class LinkerPreserve { static LinkerPreserve() { var persistentName = typeof(SQLitePersistentBlobCache).FullName; var encryptedName = typeof(SQLiteEncryptedBlobCache).FullName; } }
Ensure your UWP project targets a specific platform (x86, x64, ARM) rather than "Any CPU".
Akavache is licensed under the MIT License.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4