上一篇文章介绍了Orchard中的缓存,本篇主要针对CacheManager进行分析,CacheManager在Orchard中用于存储应用程序的配置信息以及框架内部的一些功能支持,包括整个拓展及拓展监控都是基于Cache Manager的。Orchard的中的CacheManager也是非常有特色,仅提供了一个Get接口,缓存的过期是通过IVolatileToken接口实现的。
先看一下和CacheManager的接口定义:
1 public interface ICacheManager { 2 TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire); 3 ICache<TKey, TResult> GetCache<TKey, TResult>(); 4 } 5 6 public static class CacheManagerExtensions { 7 public static TResult Get<TKey, TResult>(this ICacheManager cacheManager, TKey key, bool preventConcurrentCalls, Func<AcquireContext<TKey>, TResult> acquire) { 8 if (preventConcurrentCalls) { 9 lock(key) { 10 return cacheManager.Get(key, acquire); 11 } 12 } 13 else { 14 return cacheManager.Get(key, acquire); 15 } 16 } 17 }
从上面代码可以看出来,它仅有个Get方法是通过一个Key和一个acquire委托实现的,Key代表缓存标识,acquire代表一个获取实际值的方法,换句话说通过Key在缓存中查找对象,如果找不到从acquire中获取。acquire接受一个以AcquireContext<TKey>为参数的委托。
用下面代码做下测试:
1 public class MyController : Controller 2 { 3 private ICacheManager _cacheManager; 4 public MyController(ICacheManager cacheManager) 5 { 6 _cacheManager = cacheManager; 7 } 8 9 public ActionResult Index() 10 { 11 var time1 = _cacheManager.Get("Time", ctx => { return DateTime.Now.ToString(); }); 12 Thread.Sleep(1000); 13 var time2 = _cacheManager.Get("Time", ctx => { return DateTime.Now.ToString(); }); 14 return View(); 15 } 16 }
最后发现time1和time2的结果一致,证明第一次获取缓存的时候是通过后面的方法创建的,获取了当前的时间,第二次的值和第一次一样,证明是从缓存中取出的。
但是要如何让缓存过期?接下来看一个完整的缓存用法:
public class MyController : Controller { private ICacheManager _cacheManager; private IClock _clock; public MyController(ICacheManager cacheManager, IClock clock) { _cacheManager = cacheManager; _clock = clock; } public ActionResult Index() { var time1 = _cacheManager.Get("Time", ctx => { ctx.Monitor(_clock.When( TimeSpan.FromSeconds(5))); return DateTime.Now.ToString(); }); Thread.Sleep(1000); var time2 = _cacheManager.Get("Time", ctx => { ctx.Monitor(_clock.When(TimeSpan.FromSeconds(5))); return DateTime.Now.ToString(); }); Thread.Sleep(7000); var time3 = _cacheManager.Get("Time", ctx => { ctx.Monitor(_clock.When(TimeSpan.FromSeconds(5))); return DateTime.Now.ToString(); }); return View(); } }
上面代码的结果就是,time1 = time2 != time3。 因为time3在获取缓存时已经过期了,所以返回了后面的时间。
相比最开始的代码仅多了一个ctx.Monitor的调用就是AcquireContext<TKey>这个参数起的作用,它是如何实现的?
1 public interface IAcquireContext 2 { 3 Action<IVolatileToken> Monitor { get; } 4 } 5 6 public class AcquireContext<TKey> : IAcquireContext 7 { 8 public AcquireContext(TKey key, Action<IVolatileToken> monitor) 9 { 10 Key = key; 11 Monitor = monitor; 12 } 13 14 public TKey Key { get; private set; } 15 public Action<IVolatileToken> Monitor { get; private set; } 16 }
看上面代码可知AcquireContext(获取上下文)用于保存、映射缓存Key和它的监控委托。监控委托是一个返回值为IVolatileToken的方法。
public interface IVolatileToken { bool IsCurrent { get; } }
IVolatileToken真正用来判断当前这个Key是否过期。
接下来看一下上面代码使用的Clock:
1 /// <summary> 2 /// Provides the current Utc <see cref="DateTime"/>, and time related method for cache management. 3 /// This service should be used whenever the current date and time are needed, instead of <seealso cref="DateTime"/> directly. 4 /// It also makes implementations more testable, as time can be mocked. 5 /// </summary> 6 public interface IClock : IVolatileProvider 7 { 8 /// <summary> 9 /// Gets the current <see cref="DateTime"/> of the system, expressed in Utc 10 /// </summary> 11 DateTime UtcNow { get; } 12 13 /// <summary> 14 /// Provides a <see cref="IVolatileToken"/> instance which can be used to cache some information for a 15 /// specific duration. 16 /// </summary> 17 /// <param name="duration">The duration that the token must be valid.</param> 18 /// <example> 19 /// This sample shows how to use the <see cref="When"/> method by returning the result of 20 /// a method named LoadVotes(), which is computed every 10 minutes only. 21 /// <code> 22 /// _cacheManager.Get("votes", 23 /// ctx => { 24 /// ctx.Monitor(_clock.When(TimeSpan.FromMinutes(10))); 25 /// return LoadVotes(); 26 /// }); 27 /// </code> 28 /// </example> 29 IVolatileToken When(TimeSpan duration); 30 31 /// <summary> 32 /// Provides a <see cref="IVolatileToken"/> instance which can be used to cache some 33 /// until a specific date and time. 34 /// </summary> 35 /// <param name="absoluteUtc">The date and time that the token must be valid until.</param> 36 /// <example> 37 /// This sample shows how to use the <see cref="WhenUtc"/> method by returning the result of 38 /// a method named LoadVotes(), which is computed once, and no more until the end of the year. 39 /// <code> 40 /// var endOfYear = _clock.UtcNow; 41 /// endOfYear.Month = 12; 42 /// endOfYear.Day = 31; 43 /// 44 /// _cacheManager.Get("votes", 45 /// ctx => { 46 /// ctx.Monitor(_clock.WhenUtc(endOfYear)); 47 /// return LoadVotes(); 48 /// }); 49 /// </code> 50 /// </example> 51 IVolatileToken WhenUtc(DateTime absoluteUtc); 52 }