An asynchronous, persistent key-value store created for writing desktop and mobile applications, based on SQLite3. Akavache is great for both storing important data as well as cached local data that expires.
Under MIT License
By reactiveui

c-sharp dotnet cross-platform xamarin cache akavache reactive-extensions reactive-programming

Akavache: An Asynchronous Key-Value Store for Native Applications

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.

Where can I use it?

Akavache is currently compatible with:

What does that mean?

Downloading and storing remote data from the internet while still keeping the
UI responsive is a task that nearly every modern application needs to do.
However, many applications that don't take the consideration of caching into
the design from the start often end up with inconsistent, duplicated code for
caching different types of objects.

Akavache is a library that makes common app
patterns easy, and unifies caching of different object types (i.e. HTTP
responses vs. JSON objects vs. images).

It's built on a core key-value byte array store (conceptually similar to a
Dictionary<string, byte[]>), and on top of that store, extensions are
added to support:


Getting Started

Interacting with Akavache is primarily done through an object called
BlobCache. At App startup, you must first set your app's name via
BlobCache.ApplicationName or Akavache.Registrations.Start("ApplicationName") . After setting your app's name, you're ready to save some data.

For example with Xamarin Forms or WPF applications you'll place this in the constructor of your App.xaml.cs file.

Choose a location

There are four built-in locations that have some magic applied on some systems:

The magic

Platform-specific notes

Handling Xamarin Linker

There are two options to ensure the Akavache.Sqlite3 dll will not be removed by Xamarin build tools

1) Add a file to reference the types


public static class LinkerPreserve
static LinkerPreserve()
var persistentName = typeof(SQLitePersistentBlobCache).FullName;
var encryptedName = typeof(SQLiteEncryptedBlobCache).FullName;

2) Use the following initializer in your cross platform library or in your head project



Using Akavache

The most straightforward way to use Akavache is via the object extensions:

using System.Reactive.Linq; // IMPORTANT - this makes await work!

// Make sure you set the application name before doing any inserts or gets

var myToaster = new Toaster();
await BlobCache.UserAccount.InsertObject("toaster", myToaster);

// ...later, in another part of town...

// Using async/await
var toaster = await BlobCache.UserAccount.GetObject("toaster");

// or without async/await
Toaster toaster;

.Subscribe(x => toaster = x, ex => Console.WriteLine("No Key!"));

Handling Errors

When a key is not present in the cache, GetObject throws a
KeyNotFoundException (or more correctly, OnError's the IObservable). Often,
you would want to return a default value instead of failing:

Toaster toaster;

try {
toaster = await BlobCache.UserAccount.GetObject("toaster");
} catch (KeyNotFoundException ex) {
toaster = new Toaster();

// Or without async/await:
toaster = await BlobCache.UserAccount.GetObject("toaster")
.Catch(Observable.Return(new Toaster()));

Shutting Down

Critical to the integrity of your Akavache cache is the BlobCache.Shutdown() method. You must call this when your application shuts down. Moreover, be sure to wait for the result:


Failure to do this may mean that queued items are not flushed to the cache.

Using a different SQLitePCL.raw bundle

To use a different SQLitePCL.raw bundle, e.g. Microsoft.AppCenter:

<PackageReference Include="akavache.sqlite3" Version="6.0.40-g7e90c572c6" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="1.1.11" />

Akavache.Sqlite3.Registrations.Start("ApplicationName", () => SQLitePCL.Batteries_V2.Init());

For more info about using your own versions of SqlitePCL.raw

Examining Akavache caches

Using Akavache Explorer, you
can dig into Akavache repos for debugging purposes to see what has been stored.

What's this Global Variable nonsense?


You totally can. Just instantiate SQLitePersistentBlobCache or
SQLiteEncryptedBlobCache instead - the static variables are there just to make it
easier to get started.

DateTime/DateTimeOffset Considerations

Our default implementation overrides BSON to read and write DateTime's as UTC.
To override the reader's behavior you can set BlobCache.ForcedDateTimeKind as in the following example:

// Sets the reader to return DateTime/DateTimeOffset in Local.
BlobCache.ForcedDateTimeKind = DateTimeKind.Local;

DateTime are stored as ticks for high precision.
DateTimeOffset are stored as ticks for both the Date/Time aspect and the offset.

Basic Method Documentation

Every blob cache supports the basic raw operations given below (some of them are
not implemented directly, but are added on via extension methods):

* Get items from the store

// Get a single item
IObservable Get(string key);

// Get a list of items
IObservable> Get(IEnumerable keys);

// Get an object serialized via InsertObject
IObservable GetObject(string key);

// Get all objects of type T
IObservable> GetAllObjects();

// Get a list of objects given a list of keys
IObservable> GetObjects(IEnumerable keys);

* Save items to the store

// Insert a single item
IObservable Insert(string key, byte[] data, DateTimeOffset? absoluteExpiration = null);

// Insert a set of items
IObservable Insert(IDictionary keyValuePairs, DateTimeOffset? absoluteExpiration = null);

// Insert a single object
IObservable InsertObject(string key, T value, DateTimeOffset? absoluteExpiration = null);

// Insert a group of objects
IObservable InsertObjects(IDictionary keyValuePairs, DateTimeOffset? absoluteExpiration = null);

* Remove items from the store

// Delete a single item
IObservable Invalidate(string key);

// Delete a list of items
IObservable Invalidate(IEnumerable keys);

// Delete a single object (do not use Invalidate for items inserted with InsertObject!)
IObservable InvalidateObject(string key);

// Deletes a list of objects
IObservable InvalidateObjects(IEnumerable keys);

// Deletes all items (regardless if they are objects or not)
IObservable InvalidateAll();

// Deletes all objects of type T
IObservable InvalidateAllObjects();

* Get Metadata about items

// Return a list of all keys. Use for debugging purposes only.
IObservable> GetAllKeys();

// Return the time which an item was created
IObservable GetCreatedAt(string key);

// Return the time which an object of type T was created
IObservable GetObjectCreatedAt(string key);

// Return the time which a list of keys were created
IObservable> GetCreatedAt(IEnumerable keys);

* Utility methods

// Attempt to ensure all outstanding operations are written to disk
IObservable Flush();

// Preemptively drop all expired keys and run SQLite's VACUUM method on the
// underlying database
IObservable Vacuum();

Extension Method Documentation

On top of every IBlobCache object, there are extension methods that help with
common application scenarios:

* Username / Login Methods (only available on ISecureBlobCache)

// Save login information for the given host
IObservable SaveLogin(string user, string password, string host = "default", DateTimeOffset? absoluteExpiration = null);

// Load information for the given host
IObservable GetLoginAsync(string host = "default");

// Erase information for the given host
IObservable EraseLogin(string host = "default");

* Downloading and caching URLs and Images

// Download a file as a byte array
IObservable DownloadUrl(string url,
IDictionary headers = null,
bool fetchAlways = false,
DateTimeOffset? absoluteExpiration = null);

// Load a given key as an image
IObservable LoadImage(string key, float? desiredWidth = null, float? desiredHeight = null);

// Download an image from the network and load it
IObservable LoadImageFromUrl(string url,
bool fetchAlways = false,
float? desiredWidth = null,
float? desiredHeight = null,
DateTimeOffset? absoluteExpiration = null);

* Composite operations

// Attempt to return an object from the cache. If the item doesn't
// exist or returns an error, call a Func to return the latest
// version of an object and insert the result in the cache.
IObservable GetOrFetchObject(string key, Func> fetchFunc, DateTimeOffset? absoluteExpiration = null);

// Like GetOrFetchObject, but isn't async
IObservable GetOrCreateObject(string key, Func fetchFunc, DateTimeOffset? absoluteExpiration = null);

// Immediately return a cached version of an object if available, but always
// also execute fetchFunc to retrieve the latest version of an object.
IObservable GetAndFetchLatest(this IBlobCache This,
string key,
Func> fetchFunc,
Func fetchPredicate = null,
DateTimeOffset? absoluteExpiration = null,
bool shouldInvalidateOnError = false,
Func cacheValidationPredicate = null)