Skip to content
This repository

An asynchronous, persistent key-value store

Octocat-spinner-32 Akavache.Deprecated Update the NuGet package too February 12, 2014
Octocat-spinner-32 Akavache.Http Update the NuGet package too February 12, 2014
Octocat-spinner-32 Akavache.Mobile Update the NuGet package too February 12, 2014
Octocat-spinner-32 Akavache.Sqlite3 Retries shouldn't have to go wait in line again March 03, 2014
Octocat-spinner-32 Akavache.Tests Update the NuGet package too February 12, 2014
Octocat-spinner-32 Akavache Remove CFNetworkHttpMixin, just use the regular one March 09, 2014
Octocat-spinner-32 Akavache_WinRT.Tests Update the NuGet package too February 12, 2014
Octocat-spinner-32 component MonoMac only February 07, 2014
Octocat-spinner-32 ext Upgrade Splat for Xamarin February 12, 2014
Octocat-spinner-32 packages Check in packages, die package restore February 12, 2014
Octocat-spinner-32 tools Add nuget.exe back for release script February 10, 2014
Octocat-spinner-32 .gitattributes Update .gitignore and .gitattributes. July 18, 2013
Octocat-spinner-32 .gitignore Update release scripts to build December 27, 2013
Octocat-spinner-32 .gitmodules Add Pierce November 11, 2013
Octocat-spinner-32 Akavache.6.0.ReSharper Initial import December 01, 2011
Octocat-spinner-32 Akavache.6.1.ReSharper Add R# 6.1 file January 06, 2012
Octocat-spinner-32 Akavache.sln Remove Package Restore The Wrong Way™ February 10, 2014
Octocat-spinner-32 Akavache_XSAll.sln Fix up XS project nonsense February 07, 2014
Octocat-spinner-32 Akavache_XSNoMac.sln Merge remote-tracking branch 'origin/akavache4-master' into akavache-… December 24, 2013
Octocat-spinner-32 BuildPC.cmd Update release scripts to build December 27, 2013
Octocat-spinner-32 CommonAssemblyInfo.cs Use correct pre-4.0 version of 3.99.0-beta December 27, 2013
Octocat-spinner-32 LICENSE Add LICENSE April 23, 2012
Octocat-spinner-32 Logo.psd Add a logo for Akavache July 20, 2013
Octocat-spinner-32 MakeRelease.ps1 Add nuget.exe back for release script February 10, 2014
Octocat-spinner-32 Makefile Comes Correct. February 07, 2014
Octocat-spinner-32 PACKAGING.md Update some documentation July 20, 2013
Octocat-spinner-32 README.md Fix non-working code samples January 28, 2014
Octocat-spinner-32 akavache-meta.nuspec Version Bump to 3.99.1-beta February 07, 2014
README.md

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#. Think of it like memcached for desktop apps.

Dat Logo

Where can I use it?

Akavache is currently compatible with:

  • Xamarin.iOS / Xamarin.Mac
  • Xamarin.Android
  • .NET 4.5 Desktop (WPF)
  • Windows Phone 8
  • WinRT (Windows Store)

What does that mean?

Downloading and caching 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
  • HTTP Requests
  • Fetching and loading Images
  • Securely storing User Credentials

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 - 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:

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

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

// Using async/await
var toaster = await BlobCache.UserAccount.GetObjectAsync<Toaster>("toaster");

// or without async/await
Toaster toaster;

BlobCache.UserAccount.GetObjectAsync<Toaster>("toaster")
    .Subscribe(x => toaster = x, ex => Console.WriteLine("No Key!"));

Use SQLite3!

Akavache also ships with a separate IBlobCache implementation in the akavache.sqlite3 NuGet package which supports every platform except for WP8.

While the default implementation is still quite usable, the SQLite3 implementation has a number of speed and concurrency advantages, and is recommended for all new applications. It's not the default because it requires a native DLL (sqlite3.dll) which is a bit more work to set up. See the Sqlite3 README for more info.

To use it, simply include Akavache.Sqlite3.dll

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.GetObjectAsync<Toaster>("toaster");
} catch (KeyNotFoundException ex) {
    toaster = new Toaster();
}

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

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? Why can't I use $FAVORITE_IOC_LIBRARY

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

Unit Testing with Akavache

By default, if Akavache detects that it is being run in a unit test runner, it will use the TestBlobCache implementation instead of the default implementation. TestBlobCache implements IBlobCache in memory synchronously using a Dictionary instead of persisting to disk.

This class can be explicitly created as well, and initialized to have specific contents to test cache hit / cache miss scenarios. Use the TestBlobCache.OverrideGlobals method to temporarily replace the BlobCache.UserAccount variables with a specific TestBlobCache.

Testing expiration can also be done, using Rx's TestScheduler:

[Fact]
public void TestSomeExpirationStuff()
{
    (new TestScheduler()).With(sched => {
        using (cache = TestBlobCache.OverrideGlobals(null, sched)) {
            cache.Insert("foo", new byte[] { 1,2,3 }, TimeSpan.FromMilliseconds(100));

            sched.AdvanceByMs(50);

            var result = cache.GetAsync("foo").First();
            Assert.Equal(1, result[0]);

            // Fast forward to t=200ms, after the cache entry is expired
            sched.AdvanceByMs(150);

            bool didThrow;
            try {
                // This should now throw KeyNotFoundException
                result = cache.GetAsync("foo").First();
                didThrow = false;
            } catch (Exception ex) {
                didThrow = true;
            }

            Assert.True(didThrow);
        }
    });
}
Something went wrong with that request. Please try again.