Is there a memory cache for the portable class library?

I am creating an application that needs some kind of caching, and since my viewing models live in a portable class library, I want some kind of caching there ... but the PCL is limited and I cannot find Any.

I would like to use System.Runtime.Caching, but this is not part of PCL - are there any other options?

+6
source share
3 answers

I myself am looking for such an option; Soon, I will soon begin my own implementation and publish it. Until then, here are some ideas you can try.

Runtime.Cache, if I remember correctly, can lead to invalidation of the cache based on absolute time, sliding time and key / file control.

First, find out which one you need to implement (I need absolute and moving, although I believe that moving will be more expensive)

Take a look at this link. alternative-to-concurrentdictionary-for-portable-class-library

My idea is to extend this class, so that each cache element adds / removes, we save / delete the cache key in the dictionary object with the expiration of time (for a sliding element we will update this expiration time when reading)

Then we have one main timer that checks this dictionary so often and expires depending on the time.

If you also want, you may have other keyword dependencies depending on the dictionary, so after the key has expired, you can expired in any other cache, depending on this.

Hope this helps in some way.

--- UPDATE

I managed to get around to writing my own implementation ... here you go ..

using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; using System.Threading; namespace RanaInside.CacheProvider { internal class CacheProvider : ICacheProvider { private IDictionary<object, CacheItem> _cache = new Dictionary<object, CacheItem>(); private IDictionary<object, SliderDetails> _slidingTime = new Dictionary<object, SliderDetails>(); private const int _monitorWaitDefault = 1000; private const int _monitorWaitToUpdateSliding = 500; /// <summary> /// The sync object to make accessing the dictionary is thread safe. /// </summary> private readonly object _sync = new object(); #region Implementation of Interface public event EventHandler KeyRemoved; /// <summary> /// Add a value to the cache with a relative expiry time, eg 10 minutes. /// </summary> /// <typeparam name="TKey">The type of the key.</typeparam> /// <typeparam name="TValue">The type of the value.</typeparam> /// <param name="key">The key.</param> /// <param name="value">The value.</param> /// <param name="slidingExpiry">The sliding time when the key value pair should expire and be purged from the cache.</param> /// <param name="priority">Normal priority will be purged on low memory warning.</param> public void Add<TKey, TValue>(TKey key, TValue value, TimeSpan slidingExpiry, CacheItemPriority priority = CacheItemPriority.Normal) where TValue : class { Add(key, value, slidingExpiry, priority, true); } /// <summary> /// Add a value to the cache with an absolute time, eg 01/01/2020. /// </summary> /// <typeparam name="TKey">The type of the key.</typeparam> /// <typeparam name="TValue">The type of the value.</typeparam> /// <param name="key">The key.</param> /// <param name="value">The value.</param> /// <param name="absoluteExpiry">The absolute date time when the cache should expire and be purged the value.</param> /// <param name="priority">Normal priority will be purged on low memory warning.</param> public void Add<TKey, TValue>(TKey key, TValue value, DateTime absoluteExpiry, CacheItemPriority priority = CacheItemPriority.Normal) where TValue : class { if (absoluteExpiry < DateTime.Now) { return; } var diff = absoluteExpiry - DateTime.Now; Add(key, value, diff, priority, false); } /// <summary> /// Gets a value from the cache for specified key. /// </summary> /// <typeparam name="TKey">The type of the key.</typeparam> /// <typeparam name="TValue">The type of the value.</typeparam> /// <param name="key">The key.</param> /// <returns> /// If the key exists in the cache then the value is returned, if the key does not exist then null is returned. /// </returns> public TValue Get<TKey, TValue>(TKey key) where TValue : class { try { var cacheItem = _cache[key]; if (cacheItem.RelativeExpiry.HasValue) { if (Monitor.TryEnter(_sync, _monitorWaitToUpdateSliding)) { try { _slidingTime[key].Viewed(); } finally { Monitor.Exit(_sync); } } } return (TValue)cacheItem.Value; } catch (Exception) { return null; } } /// <summary> /// Remove a value from the cache for specified key. /// </summary> /// <typeparam name="TKey">The type of the key.</typeparam> /// <param name="key">The key.</param> public void Remove<TKey>(TKey key) { if (!Equals(key, null)) { _cache.Remove(key); _slidingTime.Remove(key); if (KeyRemoved != null) { KeyRemoved(key, new EventArgs()); } } } /// <summary> /// Clears the contents of the cache. /// </summary> public void Clear() { if (!Monitor.TryEnter(_sync, _monitorWaitDefault)) { return; } try { _cache.Clear(); _slidingTime.Clear(); } finally { Monitor.Exit(_sync); } } /// <summary> /// Gets an enumerator for keys of a specific type. /// </summary> /// <typeparam name="TKey">The type of the key.</typeparam> /// <returns> /// Returns an enumerator for keys of a specific type. /// </returns> public IEnumerable<TKey> Keys<TKey>() { if (!Monitor.TryEnter(_sync, _monitorWaitDefault)) { return Enumerable.Empty<TKey>(); } try { return _cache.Keys.Where(k => k.GetType() == typeof(TKey)).Cast<TKey>().ToList(); } finally { Monitor.Exit(_sync); } } /// <summary> /// Gets an enumerator for all the keys /// </summary> /// <returns> /// Returns an enumerator for all the keys. /// </returns> public IEnumerable<object> Keys() { if (!Monitor.TryEnter(_sync, _monitorWaitDefault)) { return Enumerable.Empty<object>(); } try { return _cache.Keys.ToList(); } finally { Monitor.Exit(_sync); } } /// <summary> /// Gets the total count of items in cache /// </summary> /// <returns> /// -1 if failed /// </returns> public int TotalItems() { if (!Monitor.TryEnter(_sync, _monitorWaitDefault)) { return -1; } try { return _cache.Keys.Count; } finally { Monitor.Exit(_sync); } } /// <summary> /// Purges all cache item with normal priorities. /// </summary> /// <returns> /// Number of items removed (-1 if failed) /// </returns> public int PurgeNormalPriorities() { if (Monitor.TryEnter(_sync, _monitorWaitDefault)) { try { var keysToRemove = (from cacheItem in _cache where cacheItem.Value.Priority == CacheItemPriority.Normal select cacheItem.Key).ToList(); return keysToRemove.Count(key => _cache.Remove(key)); } finally { Monitor.Exit(_sync); } } return -1; } #endregion #region Private class helper private void Add<TKey, TValue>(TKey key, TValue value, TimeSpan timeSpan, CacheItemPriority priority, bool isSliding) where TValue : class { if (!Monitor.TryEnter(_sync, _monitorWaitDefault)) { return; } try { // add to cache _cache.Add(key, new CacheItem(value, priority, ((isSliding) ? timeSpan : (TimeSpan?)null))); // keep sliding track if (isSliding) { _slidingTime.Add(key, new SliderDetails(timeSpan)); } StartObserving(key, timeSpan); } finally { Monitor.Exit(_sync); } } private void StartObserving<TKey>(TKey key, TimeSpan timeSpan) { Observable.Timer(timeSpan) .Finally(() => { // on finished GC.Collect(); GC.WaitForPendingFinalizers(); }) // on next .Subscribe(x => TryPurgeItem(key), exception => { // on error: Purge Failed with exception.Message }); } private void TryPurgeItem<TKey>(TKey key) { if (_slidingTime.ContainsKey(key)) { TimeSpan tryAfter; if (!_slidingTime[key].CanExpire(out tryAfter)) { // restart observing StartObserving(key, tryAfter); return; } } Remove(key); } private class CacheItem { public CacheItem() { } public CacheItem(object value, CacheItemPriority priority, TimeSpan? relativeExpiry = null) { Value = value; Priority = priority; RelativeExpiry = relativeExpiry; } public object Value { get; set; } public CacheItemPriority Priority { get; set; } public TimeSpan? RelativeExpiry { get; set; } } private class SliderDetails { public SliderDetails(TimeSpan relativeExpiry) { RelativeExpiry = relativeExpiry; Viewed(); } private TimeSpan RelativeExpiry { get; set; } private DateTime ExpireAt { get; set; } public bool CanExpire(out TimeSpan tryAfter) { tryAfter = (ExpireAt - DateTime.Now); return (0 > tryAfter.Ticks); } public void Viewed() { ExpireAt = DateTime.Now.Add(RelativeExpiry); var z = ExpireAt; } } #endregion } } 

For the latest updates, visit my blog at

http://ranahossain.blogspot.co.uk/2014/01/cache-provider-for-portable-class.html

+7
source

We had the same problem, and we created a stream data cache for our needs. It is optimized for mobile devices (similar read and write amounts), so we used a simple lock instead of ReadWriterLockSlim or other alternatives.

The idea is to add more features like events, excerpts, tags, and locks later. Currently, it supports only the region.

This is part of our LightHouse Framework and can be found on GitHub:

https://github.com/Turneo/LightHouse/blob/master/Code/Core/Core/Code/Caching/DataCache.cs

0
source

A very good alternative (but for persistent caches) is Akavache .

Akavache is an asynchronous, persistent (i.e., writable to disk) keystore designed for writing desktop and mobile C # applications based on SQLite3. Akavache is great for storing important data (for example, user settings), and for cached local data that expires.

Example snippet from the documentation:

 using System.Reactive.Linq; // IMPORTANT - this makes await work! // Make sure you set the application name before doing any inserts or gets BlobCache.ApplicationName = "AkavacheExperiment"; 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>("toaster"); // or without async/await Toaster toaster; BlobCache.UserAccount.GetObject<Toaster>("toaster") .Subscribe(x => toaster = x, ex => Console.WriteLine("No Key!")); 

Unfortunately, if, like me, you are forced to focus on .NET4 on desktop computers, then you are out of luck. But for modern applications, I would definitely go with it.

EDIT: now I see that you asked to only add chache to memory. I do not think that Akavash supports this. I am not deleting this answer, as I think that people going on Google Cache Cache may still find this useful.

0
source

Source: https://habr.com/ru/post/958690/


All Articles