簡介
ABP提供了一個緩存接口,它內部使用了這個緩存接口。雖然接口的默認實現是MemoryCache,但可以用任何其它實現的緩存供應器。Abp.RedisCache包用Redis實現了緩存(查看下方的“Redis 緩存集成”)。
ICacheManager
緩存的主要接口是ICacheManager。我們可以注入它並用它獲取一個緩存,如:
public class TestAppService : ApplicationService { private readonly ICacheManager _cacheManager; public TestAppService(ICacheManager cacheManager) { _cacheManager = cacheManager; } public Item GetItem(int id) { //Try to get from cache return _cacheManager .GetCache("MyCache") .Get(id.ToString(), () => GetFromDatabase(id)) as Item; } public Item GetFromDatabase(int id) { //... retrieve item from database } }
在此例裡,我們注入了ICacheManager,並獲得一個名為MyCache的緩存。
警告:GetCache方法
如果你的類不是單例,不要在你構造器裡使用GetCache,否則可能會銷毀你的緩存。
ICache
ICacheManager.GetCache方法返回一個ICache。一個緩存是單例的(每個緩存名)。第一次請求時創建,然後一直返回同一個緩存對象。所以,我們可以在不同的類(客戶端)裡用相同的名字共享同一個緩存。
在示例代碼裡,我們看到了ICache.get方法的簡單使用。它有兩個參數:
key:字符串,必需,一個緩存項的鍵。
factory:一個action(行為),在找不到指定key的緩存項時調用,Factory方法應該創建並返回切實的項,如果指定key的緩存已存在,就不調用。
ICache接口也有如GetOrDefault、Set、Remove和Clear等方法。同樣也有async版本。
ITypedCache
ICache接口以字符串為key,值是object類型。ITypedCache包裝了ICache並提供了類型安全、泛型。我們可用泛型的GetCache擴展方法,獲取一個ITypedCache:
ITypedCache<int, Item> myCache = _cacheManager.GetCache<int, Item>("MyCache");
同樣,我們也可用AsTyped擴展方法,把一個已存在的ICache實例轉換成ITypedCache。
配置
默認緩存超時是60分鐘,它可以改。如果你超過60分鐘沒有使用緩存中的項,會從緩存中自動移除。你可以配置指定的緩存或是全部的緩存。
//Configuration for all caches Configuration.Caching.ConfigureAll(cache => { cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2); }); //Configuration for a specific cache Configuration.Caching.Configure("MyCache", cache => { cache.DefaultSlidingExpireTime = TimeSpan.FromHours(8); });
這段代碼應該寫在你模塊的PreInitialize方法裡,用這段代碼,MyCache將有8個小時的超時時間,其它的緩存有2個小時。
在第一次創建緩存(在第一次請求)調用你的配置行為。配置不僅限於DefaultSlidingExpireTime,由於緩存對象是一個ICache,所以你可以用它的屬性和方法,自由的配置和初始化。
實體緩存
雖然ABP緩存系統出於普通的目的,但有一個EntityCache基類,可幫你緩存實體。如果我們通過它們的Id獲取的實體,我們可以用這個基類緩存它們,就不用再頻繁地從數據庫查詢。假設我們有如下所示的一個person實體:
public class Person : Entity { public string Name { get; set; } public int Age { get; set; } }
並假設我們已經知道Id,要頻繁地獲取Name。首先,我們需要創建一個類來緩存項:
[AutoMapFrom(typeof(Person))] public class PersonCacheItem { public string Name { get; set; } }
我們不應該直接在緩存裡存儲實體,由於緩存可能需要序列化緩存對象,而實體不一定能序列化(尤其有導航屬性的實體)。這就是為什麼我們創建一個簡單(像DTO:數據傳輸對象)類存儲數據。添加AutoMapFrom特性,它可以自動地把Person轉換成PersonCacheItem對象。如果我們不使用AutoMapFrom,我們應該為重載EntityCache類的MapToCacheItem方法,手動轉換/映射。
雖然不是必需,但我們可能想為我們的緩存類定義一個接口:
public interface IPersonCache : IEntityCache<PersonCacheItem> { }
最後,我們可以為實體創建緩存類:
public class PersonCache : EntityCache<Person, PersonCacheItem>, IPersonCache, ITransientDependency { public PersonCache(ICacheManager cacheManager, IRepository<Person> repository) : base(cacheManager, repository) { } }
這就是全部代碼,我們的Person緩存已經可用。緩存類可以是暫時的(如示例)或單體的,這不是說緩存的數據是暫時的,它始終在你的應用裡是全局的並線程安全的。
現在,任何需要Person的Name時,我們可以通過person的Id從緩存獲取,使用Person緩存的示例如下:
public class MyPersonService : ITransientDependency { private readonly IPersonCache _personCache; public MyPersonService(IPersonCache personCache) { _personCache = personCache; } public string GetPersonNameById(int id) { return _personCache[id].Name; //alternative: _personCache.Get(id).Name; } }
我們簡單地注入IPersonCache,獲取緩存項和獲取Name屬性。
EntityCache 是如何工作
如果你需要更復雜的緩存技術,你可以擴展EntityCache或創建你自己的解決方案。
Redis 緩存集成
默認緩存管理使用的是內存緩存。所以,如果你有多個並發的Web服務器使用同個應用,可能會成為一個問題,在這種情況下,你需要一個分布/集中緩存服務,你就可以簡單的使用Redis做為你的緩存服務器。
首先,你要在你的應用裡,安裝Abp.RedisCache的Nuget包(例如,你可在你的Web項目裡安裝)。接著為AbpRedisCacheModule添加DependsOn特性,然後在你模塊預初始化方法裡調用useRedis擴展方法。如下所示:
//...other namespaces using Abp.Runtime.Caching.Redis; namespace MyProject.AbpZeroTemplate.Web { [DependsOn( //...other module dependencies typeof(AbpRedisCacheModule))] public class MyProjectWebModule : AbpModule { public override void PreInitialize() { //...other configurations Configuration.Caching.UseRedis(); } //...other code } }
Abp.RedisCache包使用”localhost“作為默認連接字符串,你可以在配置文件裡添加連接字符串重寫它,例如:
<add name="Abp.Redis.Cache" connectionString="localhost"/>
同樣,你可以向appSettings裡添加Redis的數據庫Id,例如:
<add key="Abp.Redis.Cache.DatabaseId" value="2"/>
不同的數據庫Id,在同一服務器上,幫助創建不同的鍵空間(獨立緩存)。
UseRedis方法也有一個重載,用給定的action(行為)直接設置選項值(在配置文件中重寫)。
查看更多有關Redis信息及它的配置,請查閱Redis文檔。
提醒:Redis服務器應該安裝和運行在ABP裡。