基于DDD的.NET开发框架 - ABP缓存Caching实现
返回ABP系列
ABP是“ASP.NET Boilerplate Project (ASP.NET樣板項目)”的簡稱。
ASP.NET Boilerplate是一個用最佳實踐和流行技術(shù)開發(fā)現(xiàn)代WEB應(yīng)用程序的新起點,它旨在成為一個通用的WEB應(yīng)用程序框架和項目模板。
ABP的官方網(wǎng)站:http://www.aspnetboilerplate.com
ABP官方文檔:http://www.aspnetboilerplate.com/Pages/Documents
Github上的開源項目:https://github.com/aspnetboilerplate
一、基本概念
ABP中有兩種cache的實現(xiàn)方式:MemroyCache 和 RedisCache,兩者都繼承至ICache接口(準(zhǔn)確說是CacheBase抽象類)。ABP核心模塊封裝了MemroyCache 來實現(xiàn)ABP中的默認(rèn)緩存功能。 Abp.RedisCache這個模塊封裝RedisCache來實現(xiàn)緩存(通過StackExchange.Redis這個類庫訪問redis)。
1、ICacheManager:
緩存的主要接口是ICacheManager。注入該接口并使用該接口獲得一個緩存對象:
public class TestAppService : ApplicationService{private readonly ICacheManager _cacheManager;public TestAppService(ICacheManager cacheManager){_cacheManager = cacheManager;}public Item GetItem(int id){//從緩存中獲取return _cacheManager.GetCache("MyCache").Get(id.ToString(), () => GetFromDatabase(id)) as Item;}public Item GetFromDatabase(int id){//... 從數(shù)據(jù)庫中檢索 }}不要在構(gòu)造函數(shù)中使用GetCache方法。如果你的類是transient(每次使用都會創(chuàng)建)的,那么這可能會釋放緩存,因為第二次創(chuàng)建類的對象時,會再次調(diào)用構(gòu)造函數(shù),之前的第一次的緩存可能會被釋放。
2、ICache:
ICacheManager.GetCache方法返回一個ICache。緩存對象是單例的,第一次請求時會創(chuàng)建緩存,以后都是返回相同的緩存對象。因此,我們可以在不同的類(客戶端)中共享具有相同名字的相同緩存。
在樣例代碼中,我們看到了ICache.Get方法的簡單使用。它有兩個參數(shù):
key:緩存中一個條目的唯一字符串鍵。
factory:沒有找到給定key的緩存條目時調(diào)用的action。工廠方法應(yīng)該創(chuàng)建并返回實際的條目。如果給定的key在緩存中找到了,那么不會調(diào)用該action。ICache接口也有像GetOrDefault,Set,Remove,Clear的方法。同時,這些方法也有async版本。
3、ITypedCache:
ICache接口的key為string類型,value為object類型。ITypeCache是ICache的包裝器,提供類型安全、泛型的cache。為了將ICache轉(zhuǎn)為ITypedCache,我們可以使用AsTyped擴(kuò)展方法,如下所示:
ITypedCache<int, Item> myCache = _cacheManager.GetCache("MyCache").AsTyped<int, Item>();這樣,就不需要轉(zhuǎn)換直接可以使用Get方法。
4、配置
緩存的默認(rèn)有效期是60min。如果你在60min內(nèi)都沒有使用緩存中的元素,那么它會自動從緩存中移除。對于所有的緩存或者特定的某個緩存,你都可以配置有效期。
//為所有緩存配置有效期 Configuration.Caching.ConfigureAll(cache => {cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2); });//為特定的緩存配置有效期 Configuration.Caching.Configure("MyCache", cache => {cache.DefaultSlidingExpireTime = TimeSpan.FromHours(8); });把上面代碼放到模塊中的PreInitialize方法中。有了這樣的配置,MyCache會有8小時的有效期,而其他cache會有2小時有效期。
cache只要首次創(chuàng)建(第一次請求時),就會調(diào)用配置的action。配置并不只局限于DefaultSlidingExpireTime(默認(rèn)有效期),因為cache對象是一個ICache,你可以使用它的屬性和方法自由地配置并初始化。
5、Redis緩存集成
ABP默認(rèn)緩存管理是使用內(nèi)存緩存。可以使用Redis作為分布式緩存服務(wù)器。
首先,需要安裝abp.rediscache NuGet包添加到您的應(yīng)用程序(可以把它安裝到您的Web項目)。然后添加一個AbpRedisCacheModule依賴,在你的模塊PreInitialize配置使用:
//...引入命名空間 using Abp.Runtime.Caching.Redis; namespace MyProject.AbpZeroTemplate.Web {[DependsOn(//...模塊依賴typeof(AbpRedisCacheModule))]public class MyProjectWebModule : AbpModule{public override void PreInitialize(){//...配置Configuration.Caching.UseRedis();}//...other code } }Abp.RedisCache默認(rèn)使用“l(fā)ocalhost”作為默認(rèn)連接字符串。您可以將連接字符串添加到配置文件中以重寫它:
<add name="Abp.Redis.Cache" connectionString="localhost"/>還可以添加設(shè)置里設(shè)置Redis數(shù)據(jù)庫ID。例如:
<add key="Abp.Redis.Cache.DatabaseId" value="2"/>注:在ABP中使用Redis緩存請先安裝Redis服務(wù)器,更多redis配置信息
二、ABP緩存Caching源代碼分析
目錄結(jié)構(gòu):
類圖:
ICache:緩存的接口
using System; using System.Threading.Tasks;namespace Abp.Runtime.Caching {/// <summary>/// Defines a cache that can be store and get items by keys./// </summary>public interface ICache : IDisposable{/// <summary>/// Unique name of the cache./// </summary>string Name { get; }/// <summary>/// Default sliding expire time of cache items./// Default value: 60 minutes. Can be changed by configuration./// </summary>TimeSpan DefaultSlidingExpireTime { get; set; }/// <summary>/// Gets an item from the cache./// </summary>/// <param name="key">Key</param>/// <param name="factory">Factory method to create cache item if not exists</param>/// <returns>Cached item</returns>object Get(string key, Func<string, object> factory);/// <summary>/// Gets an item from the cache./// </summary>/// <param name="key">Key</param>/// <param name="factory">Factory method to create cache item if not exists</param>/// <returns>Cached item</returns>Task<object> GetAsync(string key, Func<string, Task<object>> factory);/// <summary>/// Gets an item from the cache or null if not found./// </summary>/// <param name="key">Key</param>/// <returns>Cached item or null if not found</returns>object GetOrDefault(string key);/// <summary>/// Gets an item from the cache or null if not found./// </summary>/// <param name="key">Key</param>/// <returns>Cached item or null if not found</returns>Task<object> GetOrDefaultAsync(string key);/// <summary>/// Saves/Overrides an item in the cache by a key./// </summary>/// <param name="key">Key</param>/// <param name="value">Value</param>/// <param name="slidingExpireTime">Sliding expire time</param>void Set(string key, object value, TimeSpan? slidingExpireTime = null);/// <summary>/// Saves/Overrides an item in the cache by a key./// </summary>/// <param name="key">Key</param>/// <param name="value">Value</param>/// <param name="slidingExpireTime">Sliding expire time</param>Task SetAsync(string key, object value, TimeSpan? slidingExpireTime = null);/// <summary>/// Removes a cache item by it's key./// </summary>/// <param name="key">Key</param>void Remove(string key);/// <summary>/// Removes a cache item by it's key (does nothing if given key does not exists in the cache)./// </summary>/// <param name="key">Key</param>Task RemoveAsync(string key);/// <summary>/// Clears all items in this cache./// </summary>void Clear();/// <summary>/// Clears all items in this cache./// </summary> Task ClearAsync();} } View CodeCacheBase:緩存基類
using System; using System.Threading.Tasks; using Nito.AsyncEx;namespace Abp.Runtime.Caching {/// <summary>/// Base class for caches./// It's used to simplify implementing <see cref="ICache"/>./// </summary>public abstract class CacheBase : ICache{public string Name { get; private set; }public TimeSpan DefaultSlidingExpireTime { get; set; }protected readonly object SyncObj = new object();private readonly AsyncLock _asyncLock = new AsyncLock();/// <summary>/// Constructor./// </summary>/// <param name="name"></param>protected CacheBase(string name){Name = name;DefaultSlidingExpireTime = TimeSpan.FromHours(1);}public virtual object Get(string key, Func<string, object> factory){var cacheKey = key;var item = GetOrDefault(key);if (item == null){lock (SyncObj){item = GetOrDefault(key);if (item == null){item = factory(key);if (item == null){return null;}Set(cacheKey, item);}}}return item;}public virtual async Task<object> GetAsync(string key, Func<string, Task<object>> factory){var cacheKey = key;var item = await GetOrDefaultAsync(key);if (item == null){using (await _asyncLock.LockAsync()){item = await GetOrDefaultAsync(key);if (item == null){item = await factory(key);if (item == null){return null;}await SetAsync(cacheKey, item);}}}return item;}public abstract object GetOrDefault(string key);public virtual Task<object> GetOrDefaultAsync(string key){return Task.FromResult(GetOrDefault(key));}public abstract void Set(string key, object value, TimeSpan? slidingExpireTime = null);public virtual Task SetAsync(string key, object value, TimeSpan? slidingExpireTime = null){Set(key, value, slidingExpireTime);return Task.FromResult(0);}public abstract void Remove(string key);public virtual Task RemoveAsync(string key){Remove(key);return Task.FromResult(0);}public abstract void Clear();public virtual Task ClearAsync(){Clear();return Task.FromResult(0);}public virtual void Dispose(){}} } View CodeITypedCache/TypedCacheWrapper: 支持泛型key和value的緩存接口與實現(xiàn),其內(nèi)部通過封裝ICache實例和CacheExtension定義的對ICache的擴(kuò)展方法來是實現(xiàn)泛型版本的Icache.
using System; using System.Threading.Tasks;namespace Abp.Runtime.Caching {/// <summary>/// Implements <see cref="ITypedCache{TKey,TValue}"/> to wrap a <see cref="ICache"/>./// </summary>/// <typeparam name="TKey"></typeparam>/// <typeparam name="TValue"></typeparam>public class TypedCacheWrapper<TKey, TValue> : ITypedCache<TKey, TValue>{public string Name{get { return InternalCache.Name; }}public TimeSpan DefaultSlidingExpireTime{get { return InternalCache.DefaultSlidingExpireTime; }set { InternalCache.DefaultSlidingExpireTime = value; }}public ICache InternalCache { get; private set; }/// <summary>/// Creates a new <see cref="TypedCacheWrapper{TKey,TValue}"/> object./// </summary>/// <param name="internalCache">The actual internal cache</param>public TypedCacheWrapper(ICache internalCache){InternalCache = internalCache;}public void Dispose(){InternalCache.Dispose();}public void Clear(){InternalCache.Clear();}public Task ClearAsync(){return InternalCache.ClearAsync();}public TValue Get(TKey key, Func<TKey, TValue> factory){return InternalCache.Get(key, factory);}public Task<TValue> GetAsync(TKey key, Func<TKey, Task<TValue>> factory){return InternalCache.GetAsync(key, factory);}public TValue GetOrDefault(TKey key){return InternalCache.GetOrDefault<TKey, TValue>(key);}public Task<TValue> GetOrDefaultAsync(TKey key){return InternalCache.GetOrDefaultAsync<TKey, TValue>(key);}public void Set(TKey key, TValue value, TimeSpan? slidingExpireTime = null){InternalCache.Set(key.ToString(), value, slidingExpireTime);}public Task SetAsync(TKey key, TValue value, TimeSpan? slidingExpireTime = null){return InternalCache.SetAsync(key.ToString(), value, slidingExpireTime);}public void Remove(TKey key){InternalCache.Remove(key.ToString());}public Task RemoveAsync(TKey key){return InternalCache.RemoveAsync(key.ToString());}} } View CodeCacheExtension: 定義了ICache的擴(kuò)展方法. 最關(guān)鍵的是如下兩個支持泛型的方法:GetOrDefault和GetOrDefaultAsync。如下,內(nèi)部調(diào)用ICache實例的相應(yīng)方法并通過類型轉(zhuǎn)換。
using System; using System.Threading.Tasks;namespace Abp.Runtime.Caching {/// <summary>/// Extension methods for <see cref="ICache"/>./// </summary>public static class CacheExtensions{public static object Get(this ICache cache, string key, Func<object> factory){return cache.Get(key, k => factory());}public static Task<object> GetAsync(this ICache cache, string key, Func<Task<object>> factory){return cache.GetAsync(key, k => factory());}public static ITypedCache<TKey, TValue> AsTyped<TKey, TValue>(this ICache cache){return new TypedCacheWrapper<TKey, TValue>(cache);}public static TValue Get<TKey, TValue>(this ICache cache, TKey key, Func<TKey, TValue> factory){return (TValue)cache.Get(key.ToString(), (k) => (object)factory(key));}public static TValue Get<TKey, TValue>(this ICache cache, TKey key, Func<TValue> factory){return cache.Get(key, (k) => factory());}public static async Task<TValue> GetAsync<TKey, TValue>(this ICache cache, TKey key, Func<TKey, Task<TValue>> factory){var value = await cache.GetAsync(key.ToString(), async (keyAsString) =>{var v = await factory(key);return (object)v;});return (TValue)value;}public static Task<TValue> GetAsync<TKey, TValue>(this ICache cache, TKey key, Func<Task<TValue>> factory){return cache.GetAsync(key, (k) => factory());}public static TValue GetOrDefault<TKey, TValue>(this ICache cache, TKey key){var value = cache.GetOrDefault(key.ToString());if (value == null){return default(TValue);}return (TValue) value;}public static async Task<TValue> GetOrDefaultAsync<TKey, TValue>(this ICache cache, TKey key){var value = await cache.GetOrDefaultAsync(key.ToString());if (value == null){return default(TValue);}return (TValue)value;}} } View CodeAbpCacheNames:定義了四個cache的key常量,這幾個cache是供ABP框架使用的
namespace Abp.Runtime.Caching {/// <summary>/// Names of standard caches used in ABP./// </summary>public static class AbpCacheNames{/// <summary>/// Application settings cache: AbpApplicationSettingsCache./// </summary>public const string ApplicationSettings = "AbpApplicationSettingsCache";/// <summary>/// Tenant settings cache: AbpTenantSettingsCache./// </summary>public const string TenantSettings = "AbpTenantSettingsCache";/// <summary>/// User settings cache: AbpUserSettingsCache./// </summary>public const string UserSettings = "AbpUserSettingsCache";/// <summary>/// Localization scripts cache: AbpLocalizationScripts./// </summary>public const string LocalizationScripts = "AbpLocalizationScripts";} } AbpCacheNamesICacheConfigurator/CacheConfigurator:封裝了cache的name和對該cahce的初始化方法,通過初始化方法可以完成對cache的配置
using System;namespace Abp.Runtime.Caching.Configuration {internal class CacheConfigurator : ICacheConfigurator{public string CacheName { get; private set; }public Action<ICache> InitAction { get; private set; }public CacheConfigurator(Action<ICache> initAction){InitAction = initAction;}public CacheConfigurator(string cacheName, Action<ICache> initAction){CacheName = cacheName;InitAction = initAction;}} } CacheConfiguratorICachingConfiguration/CachingConfiguration:?該接口提供完成cache的配置的方法。具體是通過封裝了一個ICacheConfigurator集合,并調(diào)用CacheConfigurator的InitAction來配置cache。
using System; using System.Collections.Generic; using System.Collections.Immutable; using Abp.Configuration.Startup;namespace Abp.Runtime.Caching.Configuration {internal class CachingConfiguration : ICachingConfiguration{public IAbpStartupConfiguration AbpConfiguration { get; private set; }public IReadOnlyList<ICacheConfigurator> Configurators{get { return _configurators.ToImmutableList(); }}private readonly List<ICacheConfigurator> _configurators;public CachingConfiguration(IAbpStartupConfiguration abpConfiguration){AbpConfiguration = abpConfiguration;_configurators = new List<ICacheConfigurator>();}public void ConfigureAll(Action<ICache> initAction){_configurators.Add(new CacheConfigurator(initAction));}public void Configure(string cacheName, Action<ICache> initAction){_configurators.Add(new CacheConfigurator(cacheName, initAction));}} } CachingConfigurationICacheManager/CacheManagerBase: 該接口和實現(xiàn)用于生成,配置以及銷毀ICache實例。具體是通過ICachingConfiguration對象來初始化cache, 并通過ConcurrentDictionary<string, ICache>來存放和管理cache.
AbpMemoryCache:通過MemoryCache來實現(xiàn)Icache.
AbpMemoryCacheManager:重寫了CacheManagerBase的CreateCacheImplementation方法,該方法用于創(chuàng)建真實的Icache對象。?具體到AbpMemoryCacheManager就是創(chuàng)建AbpMemoryCache。
?
總結(jié)
以上是生活随笔為你收集整理的基于DDD的.NET开发框架 - ABP缓存Caching实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [数据结构] 希尔排序
- 下一篇: 一次向svn中增加所有新增文件 svn