2013-11-23 27 views
6

我正在构建一个需要某种缓存的应用程序,由于我的模型和视图模型生活在可移植类库中,我也想要某种缓存......但PCL是有限的,我似乎找不到任何。是否有一个用于可移植类库的内存缓存?

我想使用System.Runtime.Caching,但那不包括在PCL中 - 是否还有其他选项?

回答

7

我正在寻找这样一个选项我自己;我将很快推出自己的实施,并将发布。在此之前,以下是您可能想要尝试的一些想法。

如果我没记错的话,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

0

我们遇到同样的问题,我们为我们的需要创建了线程安全的数据缓存。它针对移动设备进行了优化(类似数量的读取和写入),所以我们使用简单锁定而不是ReadWriterLockSlim或其他替代方法。

这个想法是添加更多的功能,如事件,过期,标签和稍后锁定。目前它只是支持地区。

这是我们的灯塔框架的一部分,可以在GitHub上找到:

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

0

一个很好的替代(但持续性高速缓存)是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缓存的人可能会发现它很有用。

相关问题