我正在構建一個需要某種緩存的應用程序,由於我的模型和視圖模型生活在可移植類庫中,我也想要某種緩存......但PCL是有限的,我似乎找不到任何。是否有一個用於可移植類庫的內存緩存?
我想使用System.Runtime.Caching,但那不包括在PCL中 - 是否還有其他選項?
我正在構建一個需要某種緩存的應用程序,由於我的模型和視圖模型生活在可移植類庫中,我也想要某種緩存......但PCL是有限的,我似乎找不到任何。是否有一個用於可移植類庫的內存緩存?
我想使用System.Runtime.Caching,但那不包括在PCL中 - 是否還有其他選項?
我正在尋找這樣一個選項我自己;我將很快推出自己的實施,並將發佈。在此之前,以下是您可能想要嘗試的一些想法。
如果我沒記錯的話,Runtime.Cache會根據絕對時間,滑動時間和監視鍵/文件使緩存無效。哪一個你需要實現
第一個數字(我需要絕對和滑動雖然我相信滑動會更昂貴)
看一看這個鏈接。 alternative-to-concurrentdictionary-for-portable-class-library
我的想法是,以擴展類等各個緩存項添加/刪除,我們店/刪除緩存鍵在字典中的對象與時間到期(滑動項目,我們將更新。閱讀本到期時間)
然後我們有一個主計時器,每隔一段時間檢查一次這個字典,並根據時間過期。
如果你還想要的話,你可以有另一個字典跟蹤關鍵依賴關係,所以在關鍵到期時,你可以根據這個到期其他任何緩存。
希望這有助於某種方式。
--- UPDATE
我設法避開寫我自己的實現......在這裏,你去..
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, e.g 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, e.g. 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
}
}
對於最新的更新,請訪問我的博客
http://ranahossain.blogspot.co.uk/2014/01/cache-provider-for-portable-class.html
我們遇到同樣的問題,我們爲我們的需要創建了線程安全的數據緩存。它針對移動設備進行了優化(類似數量的讀取和寫入),所以我們使用簡單鎖定而不是ReadWriterLockSlim或其他替代方法。
這個想法是添加更多的功能,如事件,過期,標籤和稍後鎖定。目前它只是支持地區。
這是我們的燈塔框架的一部分,可以在GitHub上找到:
https://github.com/Turneo/LightHouse/blob/master/Code/Core/Core/Code/Caching/DataCache.cs
一個很好的替代(但持續性高速緩存)是Akavache。
Akavache是基於SQLite3基於C#編寫桌面和移動應用程序而創建的異步持久(即寫入磁盤)鍵值存儲庫。 Akavache非常適合存儲重要數據(即用戶設置)以及過期的緩存本地數據。
來自實例文檔片段:
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!"));
不幸的是,如果像我一樣,你被迫目標.NET4在桌面上,那麼你的運氣了。但對於現代應用程序,我一定會用它。
編輯:我現在看到你問了一個內存中唯一的chache。我不認爲Akavache支持這一點。我並沒有刪除這個答案,因爲我認爲那些使用PCL緩存的人可能會發現它很有用。