上一篇文章介绍了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     }
View Code

相关文章: