{"id":13349812,"url":"https://github.com/reactiveui/Akavache","last_synced_at":"2025-03-12T09:32:38.717Z","repository":{"id":1965698,"uuid":"2896461","full_name":"reactiveui/Akavache","owner":"reactiveui","description":"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.","archived":false,"fork":false,"pushed_at":"2024-10-28T16:14:58.000Z","size":80629,"stargazers_count":2449,"open_issues_count":89,"forks_count":288,"subscribers_count":109,"default_branch":"main","last_synced_at":"2024-10-29T11:06:13.814Z","etag":null,"topics":["akavache","c-sharp","cache","cross-platform","dotnet","reactive-extensions","reactive-programming","xamarin"],"latest_commit_sha":null,"homepage":"https://evolve.xamarin.com/session/56e2044afd00c0253cae33a3","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reactiveui.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["reactivemarbles"]}},"created_at":"2011-12-02T05:15:37.000Z","updated_at":"2024-10-29T03:51:50.000Z","dependencies_parsed_at":"2023-12-02T07:04:25.183Z","dependency_job_id":"58cf0a38-d9b5-4ae9-bdf3-d12c62f372d5","html_url":"https://github.com/reactiveui/Akavache","commit_stats":{"total_commits":1431,"total_committers":61,"mean_commits":"23.459016393442624","dds":0.5220125786163522,"last_synced_commit":"8ecb41c23343ab1f162e45c55bcd6c5ea6f9fd96"},"previous_names":["github/akavache","akavache/akavache"],"tags_count":78,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiveui%2FAkavache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiveui%2FAkavache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiveui%2FAkavache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiveui%2FAkavache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reactiveui","download_url":"https://codeload.github.com/reactiveui/Akavache/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242988040,"owners_count":20217538,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["akavache","c-sharp","cache","cross-platform","dotnet","reactive-extensions","reactive-programming","xamarin"],"created_at":"2024-07-29T20:02:17.628Z","updated_at":"2025-03-12T09:32:38.318Z","avatar_url":"https://github.com/reactiveui.png","language":"C#","readme":"[![NuGet Stats](https://img.shields.io/nuget/v/akavache.svg)](https://www.nuget.org/packages/akavache) ![Build](https://github.com/reactiveui/Akavache/workflows/Build/badge.svg) [![Code Coverage](https://codecov.io/gh/reactiveui/akavache/branch/main/graph/badge.svg)](https://codecov.io/gh/reactiveui/akavache)\n\u003cbr\u003e\n\u003ca href=\"https://www.nuget.org/packages/akavache\"\u003e\n        \u003cimg src=\"https://img.shields.io/nuget/dt/akavache.svg\"\u003e\n\u003c/a\u003e\n\u003ca href=\"#backers\"\u003e\n        \u003cimg src=\"https://opencollective.com/reactiveui/backers/badge.svg\"\u003e\n\u003c/a\u003e\n\u003ca href=\"#sponsors\"\u003e\n        \u003cimg src=\"https://opencollective.com/reactiveui/sponsors/badge.svg\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://reactiveui.net/slack\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/chat-slack-blue.svg\"\u003e\n\u003c/a\u003e\n\n\u003cimg alt=\"Akavache\" src=\"https://raw.githubusercontent.com/reactiveui/styleguide/master/logo_akavache/main.png\" width=\"150\" /\u003e\n\n## Akavache: An Asynchronous Key-Value Store for Native Applications \u003c!-- omit in toc --\u003e\n\nAkavache is an *asynchronous*, *persistent* (i.e. writes to disk) key-value\nstore created for writing desktop and mobile applications in C#, based on\nSQLite3. Akavache is great for both storing important data (i.e. user\nsettings) as well as cached local data that expires.\n\n### Where can I use it? \u003c!-- omit in toc --\u003e\n\nAkavache is currently compatible with:\n\n* Xamarin.iOS / Xamarin.Mac / Xamarin.Android / Xamarin.TVOS / Xamarin.WatchOS\n* Maui iOS / Mac / Mac Catalyst / Android / TVOS\n* .NET 4.6.2 (and above) and .NET 6 Desktop (WPF and WinForms)\n* .NET 6.0\n* Windows 10 (Universal Windows Platform)\n* Tizen 4.0\n\n### What does that mean? \u003c!-- omit in toc --\u003e\n\nDownloading and storing remote data from the internet while still keeping the\nUI responsive is a task that nearly every modern application needs to do.\nHowever, many applications that don't take the consideration of caching into\nthe design from the start often end up with inconsistent, duplicated code for\ncaching different types of objects.\n\n[Akavache](https://github.com/github/akavache) is a library that makes common app\npatterns easy, and unifies caching of different object types (i.e. HTTP\nresponses vs. JSON objects vs. images).\n\nIt's built on a core key-value byte array store (conceptually similar to a\n`Dictionary\u003cstring, byte[]\u003e`), and on top of that store, extensions are\nadded to support:\n\n* Arbitrary objects via JSON.NET\n* Fetching and loading Images and URLs from the Internet\n* Storing and automatically encrypting User Credentials\n\n## Contents \u003c!-- omit in toc --\u003e\n\n* [Getting Started](#getting-started)\n  * [Choose a location](#choose-a-location)\n  * [The magic](#the-magic)\n  * [Platform-specific notes](#platform-specific-notes)\n* [Using Akavache](#using-akavache)\n  * [Handling Errors](#handling-errors)\n  * [Shutting Down](#shutting-down)\n  * [Using a different SQLitePCL.raw bundle](#using-a-different-sqlitepclraw-bundle)\n  * [Examining Akavache caches](#examining-akavache-caches)\n  * [What's this Global Variable nonsense?](#whats-this-global-variable-nonsense)\n  * [DateTime/DateTimeOffset Considerations](#datetimedatetimeoffset-considerations)\n* [Basic Method Documentation](#basic-method-documentation)\n* [Extension Method Documentation](#extension-method-documentation)\n\n## Getting Started\n\nInteracting with Akavache is primarily done through an object called\n`BlobCache`. At App startup, you must first set your app's name via\n`BlobCache.ApplicationName` or `Akavache.Registrations.Start(\"ApplicationName\")` . After setting your app's name, you're ready to save some data.\n\nFor example with Xamarin Forms or WPF applications you'll place this in the constructor of your `App.xaml.cs` file.\n\n### Choose a location\n\nThere are four built-in locations that have some magic applied on some systems:\n\n* `BlobCache.LocalMachine` - Cached data. This data may get deleted without notification.\n* `BlobCache.UserAccount` - User settings. Some systems backup this data to the cloud.\n* `BlobCache.Secure` - For saving sensitive data - like credentials.\n* `BlobCache.InMemory` - A database, kept in memory. The data is stored for the lifetime of the app.\n\n### The magic\n\n* **Xamarin.iOS** may remove data, stored in `BlobCache.LocalMachine`, to free up disk space (only if your app is not running). The locations `BlobCache.UserAccount` and `BlobCache.Secure` will be backed up to iCloud and iTunes. [Apple Documentation](https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW1)\n* **Xamarin.Android** may also start deleting data, stored in `BlobCache.LocalMachine`, if the system runs out of disk space. It isn't clearly specified if your app could be running while the system is cleaning this up. [Android Documentation](https://developer.android.com/reference/android/content/Context.html#getCacheDir%28%29)\n* **Windows 10 (UWP)** will replicate `BlobCache.UserAccount` and `BlobCache.Secure` to the cloud and synchronize it to all user devices on which the app is installed [UWP Documentation](https://docs.microsoft.com/windows/uwp/design/app-settings/store-and-retrieve-app-data)\n\n### Platform-specific notes\n\n* **Windows 10 (Universal Windows Platform)** - You must mark your application as `x86`\n  or `ARM`, or else you will get a strange runtime error about SQLitePCL_Raw not\n  loading correctly. You must *also* ensure that the Microsoft Visual C++ runtime\n  is added to your project.\n\n#### Handling Xamarin/Maui Linker\n\nThere are two options to ensure the Akavache.Sqlite3 dll will not be removed by Xamarin and Maui build tools\n\n#### 1) Add a file to reference the types\n\n```cs\n\npublic static class LinkerPreserve\n{\n  static LinkerPreserve()\n  {\n    var persistentName = typeof(SQLitePersistentBlobCache).FullName;\n    var encryptedName = typeof(SQLiteEncryptedBlobCache).FullName;\n  }\n}\n```\n\n#### 2) Use the following initializer in your cross platform library or in your head project\n\n```cs\n\nAkavache.Registrations.Start(\"ApplicationName\")\n```\n\n## Using Akavache\n\nThe most straightforward way to use Akavache is via the object extensions:\n\n```cs\nusing System.Reactive.Linq;   // IMPORTANT - this makes await work!\n\n// Make sure you set the application name before doing any inserts or gets\nAkavache.Registrations.Start(\"AkavacheExperiment\")\n\nvar myToaster = new Toaster();\nawait BlobCache.UserAccount.InsertObject(\"toaster\", myToaster);\n\n//\n// ...later, in another part of town...\n//\n\n// Using async/await\nvar toaster = await BlobCache.UserAccount.GetObject\u003cToaster\u003e(\"toaster\");\n\n// or without async/await\nToaster toaster;\n\nBlobCache.UserAccount.GetObject\u003cToaster\u003e(\"toaster\")\n    .Subscribe(x =\u003e toaster = x, ex =\u003e Console.WriteLine(\"No Key!\"));\n```\n\n### Handling Errors\n\nWhen a key is not present in the cache, GetObject throws a\nKeyNotFoundException (or more correctly, OnError's the IObservable). Often,\nyou would want to return a default value instead of failing:\n\n```cs\nToaster toaster;\n\ntry {\n    toaster = await BlobCache.UserAccount.GetObject(\"toaster\");\n} catch (KeyNotFoundException ex) {\n    toaster = new Toaster();\n}\n\n// Or without async/await:\ntoaster = await BlobCache.UserAccount.GetObject\u003cToaster\u003e(\"toaster\")\n    .Catch(Observable.Return(new Toaster()));\n```\n\n### Shutting Down\n\nCritical 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:\n\n```cs\nBlobCache.Shutdown().Wait();\n```\n\nFailure to do this may mean that queued items are not flushed to the cache.\n\n### Using a different SQLitePCL.raw bundle\n\nTo use a different SQLitePCL.raw bundle, e.g. Microsoft.AppCenter:\n\n* Install the `akavache.sqlite3` nuget instead of `akavache`\n* Install the SQLitePCLRaw bundle you want to use, e.g., `SQLitePCLRaw.bundle_green`\n* Use `Akavache.Sqlite3.Registrations.Start(\"ApplicationName\", () =\u003e SQLitePCL.Batteries_V2.Init());` in your platform projects or in your cross platform project.\n\n```XAML\n\u003cPackageReference Include=\"akavache.sqlite3\" Version=\"6.0.40-g7e90c572c6\" /\u003e\n\u003cPackageReference Include=\"SQLitePCLRaw.bundle_green\" Version=\"1.1.11\" /\u003e\n```\n\n```cs\nAkavache.Sqlite3.Registrations.Start(\"ApplicationName\", () =\u003e SQLitePCL.Batteries_V2.Init());\n```\n\nFor more info about using your own versions of [SqlitePCL.raw](https://github.com/ericsink/SQLitePCL.raw/wiki/Using-multiple-libraries-that-use-SQLitePCL.raw)\n\n### Examining Akavache caches\n\nUsing [Akavache Explorer](https://github.com/anaisbetts/AkavacheExplorer), you\ncan dig into Akavache repos for debugging purposes to see what has been stored.\n\n![](http://f.cl.ly/items/2D3Y0L0k262X0U0y3B0e/Image%202012.05.07%206:57:48%20PM.png)\n\n### What's this Global Variable nonsense?\n\n**Why can't I use $FAVORITE_IOC_LIBRARY?**\n\nYou totally can. Just instantiate `SQLitePersistentBlobCache` or\n`SQLiteEncryptedBlobCache` instead - the static variables are there just to make it\neasier to get started.\n\n### DateTime/DateTimeOffset Considerations\n\nOur default implementation overrides BSON to read and write DateTime's as UTC.\nTo override the reader's behavior you can set `BlobCache.ForcedDateTimeKind` as in the following example:\n\n```cs\n// Sets the reader to return DateTime/DateTimeOffset in Local.\nBlobCache.ForcedDateTimeKind = DateTimeKind.Local;\n```\n\n`DateTime` are stored as ticks for high precision.\n`DateTimeOffset` are stored as ticks for both the Date/Time aspect and the offset.\n\n## Basic Method Documentation\n\nEvery blob cache supports the basic raw operations given below (some of them are\nnot implemented directly, but are added on via extension methods):\n\n```cs\n/*\n * Get items from the store\n */\n\n// Get a single item\nIObservable\u003cbyte[]\u003e Get(string key);\n\n// Get a list of items\nIObservable\u003cIDictionary\u003cstring, byte[]\u003e\u003e Get(IEnumerable\u003cstring\u003e keys);\n\n// Get an object serialized via InsertObject\nIObservable\u003cT\u003e GetObject\u003cT\u003e(string key);\n\n// Get all objects of type T\nIObservable\u003cIEnumerable\u003cT\u003e\u003e GetAllObjects\u003cT\u003e();\n\n// Get a list of objects given a list of keys\nIObservable\u003cIDictionary\u003cstring, T\u003e\u003e GetObjects\u003cT\u003e(IEnumerable\u003cstring\u003e keys);\n\n/*\n * Save items to the store\n */\n\n// Insert a single item\nIObservable\u003cUnit\u003e Insert(string key, byte[] data, DateTimeOffset? absoluteExpiration = null);\n\n// Insert a set of items\nIObservable\u003cUnit\u003e Insert(IDictionary\u003cstring, byte[]\u003e keyValuePairs, DateTimeOffset? absoluteExpiration = null);\n\n// Insert a single object\nIObservable\u003cUnit\u003e InsertObject\u003cT\u003e(string key, T value, DateTimeOffset? absoluteExpiration = null);\n\n// Insert a group of objects\nIObservable\u003cUnit\u003e InsertObjects\u003cT\u003e(IDictionary\u003cstring, T\u003e keyValuePairs, DateTimeOffset? absoluteExpiration = null);\n\n/*\n * Remove items from the store\n */\n\n// Delete a single item\nIObservable\u003cUnit\u003e Invalidate(string key);\n\n// Delete a list of items\nIObservable\u003cUnit\u003e Invalidate(IEnumerable\u003cstring\u003e keys);\n\n// Delete a single object (do *not* use Invalidate for items inserted with InsertObject!)\nIObservable\u003cUnit\u003e InvalidateObject\u003cT\u003e(string key);\n\n// Deletes a list of objects\nIObservable\u003cUnit\u003e InvalidateObjects\u003cT\u003e(IEnumerable\u003cstring\u003e keys);\n\n// Deletes all items (regardless if they are objects or not)\nIObservable\u003cUnit\u003e InvalidateAll();\n\n// Deletes all objects of type T\nIObservable\u003cUnit\u003e InvalidateAllObjects\u003cT\u003e();\n\n/*\n * Get Metadata about items\n */\n\n// Return a list of all keys. Use for debugging purposes only.\nIObservable\u003cIEnumerable\u003cstring\u003e\u003e GetAllKeys();\n\n// Return the time which an item was created\nIObservable\u003cDateTimeOffset?\u003e GetCreatedAt(string key);\n\n// Return the time which an object of type T was created\nIObservable\u003cDateTimeOffset?\u003e GetObjectCreatedAt\u003cT\u003e(string key);\n\n// Return the time which a list of keys were created\nIObservable\u003cIDictionary\u003cstring, DateTimeOffset?\u003e\u003e GetCreatedAt(IEnumerable\u003cstring\u003e keys);\n\n/*\n * Utility methods\n */\n\n// Attempt to ensure all outstanding operations are written to disk\nIObservable\u003cUnit\u003e Flush();\n\n// Preemptively drop all expired keys and run SQLite's VACUUM method on the\n// underlying database\nIObservable\u003cUnit\u003e Vacuum();\n```\n\n## Extension Method Documentation\n\nOn top of every `IBlobCache` object, there are extension methods that help with\ncommon application scenarios:\n\n```cs\n/*\n * Username / Login Methods (only available on ISecureBlobCache)\n */\n\n// Save login information for the given host\nIObservable\u003cUnit\u003e SaveLogin(string user, string password, string host = \"default\", DateTimeOffset? absoluteExpiration = null);\n\n// Load information for the given host\nIObservable\u003cLoginInfo\u003e GetLoginAsync(string host = \"default\");\n\n// Erase information for the given host\nIObservable\u003cUnit\u003e EraseLogin(string host = \"default\");\n\n/*\n * Downloading and caching URLs and Images\n */\n\n// Download a file as a byte array\nIObservable\u003cbyte[]\u003e DownloadUrl(string url,\n    IDictionary\u003cstring, string\u003e headers = null,\n    bool fetchAlways = false,\n    DateTimeOffset? absoluteExpiration = null);\n\n// Load a given key as an image\nIObservable\u003cIBitmap\u003e LoadImage(string key, float? desiredWidth = null, float? desiredHeight = null);\n\n// Download an image from the network and load it\nIObservable\u003cIBitmap\u003e LoadImageFromUrl(string url,\n    bool fetchAlways = false,\n    float? desiredWidth = null,\n    float? desiredHeight = null,\n    DateTimeOffset? absoluteExpiration = null);\n\n/*\n * Composite operations\n */\n\n// Attempt to return an object from the cache. If the item doesn't\n// exist or returns an error, call a Func to return the latest\n// version of an object and insert the result in the cache.\nIObservable\u003cT\u003e GetOrFetchObject\u003cT\u003e(string key, Func\u003cTask\u003cT\u003e\u003e fetchFunc, DateTimeOffset? absoluteExpiration = null);\n\n// Like GetOrFetchObject, but isn't async\nIObservable\u003cT\u003e GetOrCreateObject\u003cT\u003e(string key, Func\u003cT\u003e fetchFunc, DateTimeOffset? absoluteExpiration = null);\n\n// Immediately return a cached version of an object if available, but *always*\n// also execute fetchFunc to retrieve the latest version of an object.\nIObservable\u003cT\u003e GetAndFetchLatest\u003cT\u003e(this IBlobCache This,\n    string key,\n    Func\u003cIObservable\u003cT\u003e\u003e fetchFunc,\n    Func\u003cDateTimeOffset, bool\u003e fetchPredicate = null,\n    DateTimeOffset? absoluteExpiration = null,\n    bool shouldInvalidateOnError = false,\n    Func\u003cT, bool\u003e cacheValidationPredicate = null)\n```\n","funding_links":["https://github.com/sponsors/reactivemarbles"],"categories":["Caching","缓存","C# #","Reactive"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactiveui%2FAkavache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freactiveui%2FAkavache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactiveui%2FAkavache/lists"}