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 is currently compatible with:
- Xamarin.iOS / Xamarin.Mac 32-bit
- Xamarin.Android
- .NET 4.5 Desktop (WPF)
- Windows Phone 8
- WinRT (Windows Store)
- Windows Phone 8.1 Universal Apps
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:
- Arbitrary objects via JSON.NET
- Fetching and loading Images and URLs from the Internet
- Storing and automatically encrypting User Credentials
Xamarin.iOS / Xamarin.Mac 32-bit - No issues.
Xamarin.Android - No issues.
.NET 4.5 Desktop (WPF) - No issues
Windows Phone 8.0 - You must mark your application as
x86orARM, or else you will get a strange runtime error about SQLitePCL_Raw not loading correctly.WinRT (Windows Store) - You must mark your application as
x86orARM, or else you will get a strange runtime error about SQLitePCL_Raw not loading correctly. You must also ensure that the Microsoft Visual C++ runtime is added to your project. This means that you must submit several versions of your app to the Store to support ARM.Windows Phone 8.1 Universal Apps - You must mark your application as
x86orARM, or else you will get a strange runtime error about SQLitePCL_Raw not loading correctly. You must also ensure that the Microsoft Visual C++ runtime is added to your project.
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 - on the desktop, your application's data will be
stored in %AppData%\[ApplicationName] and
%LocalAppData%\[ApplicationName]. Store data that should be shared between
different machines in BlobCache.UserAccount and store data that is
throwaway or per-machine (such as images) in BlobCache.LocalMachine.
The most straightforward way to use Akavache is via the object extensions:
usingSystem.Reactive.Linq;// IMPORTANT - this makes await work!// Make sure you set the application name before doing any inserts or getsBlobCache.ApplicationName="AkavacheExperiment";varmyToaster=newToaster();awaitBlobCache.UserAccount.InsertObject("toaster",myToaster);//// ...later, in another part of town...//// Using async/awaitvartoaster=awaitBlobCache.UserAccount.GetObject<Toaster>("toaster");// or without async/awaitToastertoaster;BlobCache.UserAccount.GetObject<Toaster>("toaster").Subscribe(x =>toaster=x, ex =>Console.WriteLine("No Key!"));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:
Toastertoaster;try{toaster=awaitBlobCache.UserAccount.GetObjectAsync("toaster");}catch(KeyNotFoundExceptionex){toaster=newToaster();}// Or without async/await:toaster=awaitBlobCache.UserAccount.GetObjectAsync<Toaster>("toaster").Catch(Observable.Return(newToaster()));Using Akavache Explorer, you can dig into Akavache repos for debugging purposes to see what has been stored.
You totally can. Just instantiate SQLitePersistentBlobCache or
SQLiteEncryptedBlobCache instead - the static variables are there just to make it
easier to get started.
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 itemIObservable<byte[]>Get(stringkey);// Get a list of itemsIObservable<IDictionary<string,byte[]>>Get(IEnumerable<string>keys);// Get an object serialized via InsertObjectIObservable<T>GetObject<T>(stringkey);// Get all objects of type TIObservable<IEnumerable<T>>GetAllObjects<T>();// Get a list of objects given a list of keysIObservable<IDictionary<string,T>>GetObjects<T>(IEnumerable<string>keys);/* * Save items to the store */// Insert a single itemIObservable<Unit>Insert(stringkey,byte[]data,DateTimeOffset?absoluteExpiration=null);// Insert a set of itemsIObservable<Unit>Insert(IDictionary<string,byte[]>keyValuePairs,DateTimeOffset?absoluteExpiration=null);// Insert a single objectIObservable<Unit>InsertObject<T>(stringkey,Tvalue,DateTimeOffset?absoluteExpiration=null);// Insert a group of objectsIObservable<Unit>InsertObjects<T>(IDictionary<string,T>keyValuePairs,DateTimeOffset?absoluteExpiration=null);/* * Remove items from the store */// Delete a single itemIObservable<Unit>Invalidate(stringkey);// Delete a list of itemsIObservable<Unit>Invalidate(IEnumerable<string>keys);// Delete a single object (do *not* use Invalidate for items inserted with InsertObject!)IObservable<Unit>InvalidateObject<T>(stringkey);// Deletes a list of objectsIObservable<Unit>InvalidateObjects<T>(IEnumerable<string>keys);// Deletes all items (regardless if they are objects or not)IObservable<Unit>InvalidateAll();// Deletes all objects of type TIObservable<Unit>InvalidateAllObjects<T>();/* * Get Metadata about items */// Return a list of all keys. Use for debugging purposes only.IObservable<IEnumerable<string>>GetAllKeys();// Return the time which an item was createdIObservable<DateTimeOffset?>GetCreatedAt(stringkey);// Return the time which an object of type T was createdIObservable<DateTimeOffset?>GetObjectCreatedAt<T>(stringkey);// Return the time which a list of keys were createdIObservable<IDictionary<string,DateTimeOffset?>>GetCreatedAt(IEnumerable<string>keys);/* * Utility methods */// Attempt to ensure all outstanding operations are written to diskIObservable<Unit>Flush();// Preemptively drop all expired keys and run SQLite's VACUUM method on the// underlying databaseIObservable<Unit>Vacuum();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 hostIObservable<Unit>SaveLogin(stringuser,stringpassword,stringhost="default",DateTimeOffset?absoluteExpiration=null);// Load information for the given hostIObservable<LoginInfo>GetLoginAsync(stringhost="default");// Erase information for the given hostIObservable<Unit>EraseLogin(stringhost="default");/* * Downloading and caching URLs and Images */// Download a file as a byte arrayIObservable<byte[]>DownloadUrl(stringurl,IDictionary<string,string>headers=null,boolfetchAlways=false,DateTimeOffset?absoluteExpiration=null);// Load a given key as an imageIObservable<IBitmap>LoadImage(stringkey,float?desiredWidth=null,float?desiredHeight=null);// Download an image from the network and load itIObservable<IBitmap>LoadImageFromUrl(stringurl,boolfetchAlways=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<T>GetOrFetchObject<T>(stringkey,Func<Task<T>>fetchFunc,DateTimeOffset?absoluteExpiration=null);// Like GetOrFetchObject, but isn't asyncIObservable<T>GetOrCreateObject<T>(stringkey,Func<T>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<T>GetAndFetchLatest<T>(stringkey,Func<Task<T>>fetchFunc,Func<DateTimeOffset,bool>fetchPredicate=null,DateTimeOffset?absoluteExpiration=null);
