返回ABP系列html
ABP是「ASP.NET Boilerplate Project (ASP.NET樣板項目)」的簡稱。git
ASP.NET Boilerplate是一個用最佳實踐和流行技術開發現代WEB應用程序的新起點,它旨在成爲一個通用的WEB應用程序框架和項目模板。github
ABP的官方網站:http://www.aspnetboilerplate.comredis
ABP官方文檔:http://www.aspnetboilerplate.com/Pages/Documents數據庫
Github上的開源項目:https://github.com/aspnetboilerplate緩存
ABP中有兩種cache的實現方式:MemroyCache 和 RedisCache,二者都繼承至ICache接口(準確說是CacheBase抽象類)。ABP核心模塊封裝了MemroyCache 來實現ABP中的默認緩存功能。 Abp.RedisCache這個模塊封裝RedisCache來實現緩存(經過StackExchange.Redis這個類庫訪問redis)。安全
一、ICacheManager:服務器
緩存的主要接口是ICacheManager。注入該接口並使用該接口得到一個緩存對象:app
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) { //... 從數據庫中檢索 } }
不要在構造函數中使用GetCache方法。若是你的類是transient(每次使用都會建立)的,那麼這可能會釋放緩存,由於第二次建立類的對象時,會再次調用構造函數,以前的第一次的緩存可能會被釋放。框架
二、ICache:
ICacheManager.GetCache方法返回一個ICache。緩存對象是單例的,第一次請求時會建立緩存,之後都是返回相同的緩存對象。所以,咱們能夠在不一樣的類(客戶端)中共享具備相同名字的相同緩存。
在樣例代碼中,咱們看到了ICache.Get方法的簡單使用。它有兩個參數:
key:緩存中一個條目的惟一字符串鍵。
factory:沒有找到給定key的緩存條目時調用的action。工廠方法應該建立並返回實際的條目。若是給定的key在緩存中找到了,那麼不會調用該action。ICache接口也有像GetOrDefault,Set,Remove,Clear的方法。同時,這些方法也有async版本。
三、ITypedCache:
ICache接口的key爲string類型,value爲object類型。ITypeCache是ICache的包裝器,提供類型安全、泛型的cache。爲了將ICache轉爲ITypedCache,咱們能夠使用AsTyped擴展方法,以下所示:
ITypedCache<int, Item> myCache = _cacheManager.GetCache("MyCache").AsTyped<int, Item>();
這樣,就不須要轉換直接能夠使用Get方法。
四、配置
緩存的默認有效期是60min。若是你在60min內都沒有使用緩存中的元素,那麼它會自動從緩存中移除。對於全部的緩存或者特定的某個緩存,你均可以配置有效期。
//爲全部緩存配置有效期 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只要首次建立(第一次請求時),就會調用配置的action。配置並不僅侷限於DefaultSlidingExpireTime(默認有效期),由於cache對象是一個ICache,你能夠使用它的屬性和方法自由地配置並初始化。
五、Redis緩存集成
ABP默認緩存管理是使用內存緩存。能夠使用Redis做爲分佈式緩存服務器。
首先,須要安裝abp.rediscache NuGet包添加到您的應用程序(能夠把它安裝到您的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默認使用「localhost」做爲默認鏈接字符串。您能夠將鏈接字符串添加到配置文件中以重寫它:
<add name="Abp.Redis.Cache" connectionString="localhost"/>
還能夠添加設置裏設置Redis數據庫ID。例如:
<add key="Abp.Redis.Cache.DatabaseId" value="2"/>
注:在ABP中使用Redis緩存請先安裝Redis服務器,更多redis配置信息
目錄結構:
類圖:
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(); } }
CacheBase:緩存基類
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() { } } }
ITypedCache/TypedCacheWrapper: 支持泛型key和value的緩存接口與實現,其內部經過封裝ICache實例和CacheExtension定義的對ICache的擴展方法來是實現泛型版本的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()); } } }
CacheExtension: 定義了ICache的擴展方法. 最關鍵的是以下兩個支持泛型的方法:GetOrDefault和GetOrDefaultAsync。以下,內部調用ICache實例的相應方法並經過類型轉換。
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; } } }
AbpCacheNames:定義了四個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"; } }
ICacheConfigurator/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; } } }
ICachingConfiguration/CachingConfiguration: 該接口提供完成cache的配置的方法。具體是經過封裝了一個ICacheConfigurator集合,並調用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)); } } }
ICacheManager/CacheManagerBase: 該接口和實現用於生成,配置以及銷燬ICache實例。具體是經過ICachingConfiguration對象來初始化cache, 並經過ConcurrentDictionary<string, ICache>來存放和管理cache.
AbpMemoryCache:經過MemoryCache來實現Icache.
AbpMemoryCacheManager:重寫了CacheManagerBase的CreateCacheImplementation方法,該方法用於建立真實的Icache對象。 具體到AbpMemoryCacheManager就是建立AbpMemoryCache。