Akavache: an asynchronous, SQLite-backed key-value store for .NET apps
An asynchronous, persistent key-value store for .NET desktop and mobile applications, backed by SQLite. Suited to both durable data such as user settings and cached data that expires, with optional encryption and System.Text.Json serialization.
At a glance
- What is it?
- Akavache gives .NET desktop and mobile apps a persistent key-value cache with expiration, encryption and Reactive Extensions style APIs. This review covers the V12 SQLite rewrite, how to install it, and where it stops being the right tool.
- Who is it for?
- Adopt Akavache if you are writing a .NET desktop or mobile app that needs durable local settings, expiring API responses and encrypted secrets, and you want them behind one async key-value API rather than hand-rolled SQLite code. Do not adopt it if you need a relational query surface, a server-side shared cache, or a store whose on-disk format you can inspect with generic SQLite tooling.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Akavache solves, and for whom
A .NET mobile or desktop app accumulates local state: user preferences, session tokens, API responses you want to show instantly on next launch, images you do not want to re-download. The naive answer is to write files or open SQLite directly, and that answer gets messy fast once you add expiration, encryption and concurrent access from several threads. Akavache packages that work behind a small key-value surface. The README describes it as an asynchronous, persistent key-value store for desktop and mobile applications in C#, based on SQLite3, suited both to important data such as user settings and to cached local data that expires. The intended audience is app developers who already work in the ReactiveUI and Splat ecosystem, or who are willing to pull Splat in for initialization. The topics list confirms the framing: akavache, c-sharp, cache, cross-platform, dotnet, reactive-extensions, reactive-programming, xamarin.
Inside the V12 SQLite backend and its data flow
The V12 release notes describe a rewrite of the SQLite backend for direct SQLitePCLRaw 3.x access, replacing the sqlite-net-pcl ORM layer entirely. That change is the most interesting engineering decision in the project. Instead of mapping objects through an ORM, Akavache caches and reuses prepared statements and binds parameters positionally. All native handle access is serialized onto a dedicated worker thread, and concurrent writes are coalesced into a single commit. The claimed effects are lower per-operation allocations and fewer commit round trips. None of that is independently verified here, but the architecture is coherent: an ORM buys you query flexibility, and a key-value store does not need query flexibility, so dropping the ORM removes work the store was never going to use.
The data flow is straightforward. A caller writes a value through one of the cache properties, the configured serializer turns it into bytes, and the SQLite provider stores it under a key with an optional expiration. Reads go the other way, with expiration checked on retrieval. Serialization is pluggable: the repository ships Akavache.SystemTextJson, Akavache.SystemTextJson.Bson, and Akavache.NewtonsoftJson as separate packages, and the README states that the pure JSON package no longer pulls in Newtonsoft.Json. The V12 notes also mention JsonTypeInfo<T> overloads for System.Text.Json, described as trim-safe, which matters if you publish with trimming or AOT. SettingsBase properties are now IObservable<T>, a change the notes frame as removing .Wait() deadlocks.
Installing Akavache and writing your first cached value
The README gives the package pair to start with: the SQLite backend plus a serializer. A storage backend and a serializer are both required, so the core package alone is not enough for persistence.
<PackageReference Include="Akavache.Sqlite3" Version="*" />
<PackageReference Include="Akavache.SystemTextJson" Version="*" />Initialization goes through Splat's builder. The README is emphatic that WithAkavache, WithAkavacheCacheDatabase and Initialize always require an ISerializer supplied as a generic type, so the call is parameterized on SystemJsonSerializer rather than configured at runtime. Note the ordering constraint: WithSqliteProvider() must be called explicitly before WithSqliteDefaults(). The README says WithSqliteDefaults() will call WithSqliteProvider() automatically for backward compatibility, but labels that automatic behavior deprecated and possibly removed in future versions.
using Akavache.Core;
using Akavache.SystemTextJson;
using Akavache.Sqlite3;
using Splat.Builder;
AppBuilder.CreateSplatBuilder()
.WithAkavacheCacheDatabase<SystemJsonSerializer>(builder =>
builder.WithSqliteProvider()
.WithSqliteDefaults(),
"MyApp");With the cache initialized, the README's basic operations show insert and read by key, plus the get-or-fetch pattern that is the reason many people reach for this library in the first place: pass a key, a factory, and let the cache decide whether to call the factory.
var user = new User { Name = "John", Email = "[email protected]" };
await CacheDatabase.UserAccount.InsertObject("current_user", user);
var cachedUser = await CacheDatabase.UserAccount.GetObject<User>("current_user");
var data = await CacheDatabase.LocalMachine.GetOrFetchObject("api_data",
async () => await httpClient.GetFromJsonAsync<ApiResponse>("https://api.example.com/data"));The four cache properties are not interchangeable. UserAccount is for settings and preferences that persist. LocalMachine is for data the system can safely delete. Secure is encrypted storage for credentials and API keys, with a documented SaveLogin call taking a user, a password and a host. InMemory does not survive the session. Expiration is passed as a DateTimeOffset on insert, as in the README's one-hour example.
Where Akavache is the wrong choice
The store is key-value. There is no query language, no joins, no indexes you define yourself. If you need to ask questions like which records were modified after a timestamp, or aggregate rows, you are using the wrong layer, and the fact that SQLite sits underneath does not help you, because the V12 backend binds positional parameters against prepared statements rather than exposing SQL.
The README does not document rollback, schema migration for your own data, or what happens when a write fails midway. It also does not describe how expiration is enforced in the background, if at all, so a reader should not assume expired entries are purged on a schedule rather than filtered on read. The migration guide exists as docs/migration-v11-to-v12.md, which tells you the V11 to V12 move is treated as a real migration rather than a drop-in, and the V12 notes list breaking changes including the serializer package split and the SQLite3MultipleCiphers swap for encrypted databases. Anyone on V11 with encrypted data should read that document before upgrading, because the encryption backend changed.
There is also a packaging trap. The README's own table lists nine packages across core, storage, serializers, HTTP downloading and drawing. Choosing the wrong combination, or expecting Newtonsoft.Json to arrive transitively, produces failures that look like configuration problems rather than missing dependencies.
Akavache compared with EF Core and Microsoft.Extensions.Caching
The closest alternative for local persistence in .NET is EF Core over SQLite. The difference is the abstraction level. EF Core models entities and relationships and gives you LINQ queries, migrations and change tracking; Akavache models keys and values and gives you expiration, encryption and a get-or-fetch call. If your local data has shape and relationships, EF Core is the honest choice, and you will pay for it in startup cost and mapping code. If your local data is a bag of settings and cached responses, Akavache removes most of the code you would otherwise write.
For pure in-process caching, Microsoft.Extensions.Caching.Memory is lighter: no disk, no native SQLite dependency, no serializer choice. It also loses everything on restart, which is exactly the property Akavache's persistence is there to provide. Akavache's InMemory cache type covers the same ground inside the same API, so the two can coexist without a second mental model.
The Reactive Extensions angle is the real differentiator. Akavache's APIs return observables, and the V12 notes describe SettingsBase properties as IObservable<T>. If your app already composes with Rx, values from the cache fit into existing pipelines. If it does not, the Rx surface is overhead you will work around.
Maintenance, licensing and the cost of upgrading
The repository is not archived, and the last push was on 2026-09-25. Releases are frequent enough to matter: v13.0.0 on 2026-08-07, v12.1.1 on 2026-06-03, and 12.0.1-beta on 2026-04-19. The V12 line introduced the SQLitePCLRaw rewrite and the SQLite3MultipleCiphers change, and V13 followed within months, so the upgrade cost is not zero. Each major line has its own migration document, and the V11 to V12 guide is referenced directly from the README.
The licence is MIT, which permits commercial and closed-source use and modification provided the copyright notice and permission notice are included. That is a permissive licence with few obligations, but the SQLitePCLRaw and SQLite3MultipleCiphers dependencies carry their own licences, and the README does not enumerate them. Check the transitive licence set for your distribution model, particularly on mobile stores. Nothing here is legal advice.
Editorial conclusion
Adopt Akavache if you are writing a .NET desktop or mobile app that needs durable local settings, expiring API responses and encrypted secrets, and you want them behind one async key-value API rather than hand-rolled SQLite code. Do not adopt it if you need a relational query surface, a server-side shared cache, or a store whose on-disk format you can inspect with generic SQLite tooling. Before committing, verify three things: that the package set you install matches the serializer you intend to use, that WithSqliteProvider() runs before WithSqliteDefaults() in your builder chain, and that the SQLitePCLRaw 3.x native dependency resolves on every platform you ship.
Frequently asked questions
Which NuGet packages do I need to install for Akavache with SQLite persistence?
At minimum you need the core package plus a storage backend and a serializer. For SQLite persistence with System.Text.Json, the README shows Akavache.Sqlite3 together with Akavache.SystemTextJson. Encrypted persistence uses Akavache.EncryptedSqlite3 instead.
How do I initialize Akavache in a .NET application?
Use the Splat builder: AppBuilder.CreateSplatBuilder().WithAkavacheCacheDatabase<SystemJsonSerializer>(builder => builder.WithSqliteProvider().WithSqliteDefaults(), "MyApp"). The serializer must be supplied as a generic type, and the README recommends calling WithSqliteProvider() explicitly before WithSqliteDefaults() because the automatic fallback is deprecated.
Does Akavache support encryption for sensitive data?
Yes. The Secure cache is described as encrypted storage for sensitive data such as credentials and API keys, and the README shows SaveLogin for storing a login. The V12 notes state that encrypted databases now use SQLite3MultipleCiphers instead of sqlcipher, so encrypted data written under V11 needs the migration guide.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/reactiveui-akavache)