缓存
ABP为缓存提供了一个抽象接口,它内部使用这个缓存抽象。默认使用MemoryCache实现,但可以换用其他的缓存提供者实现。例如Abp.RedisCache包实现了在Redis中实现缓存。(参见下面的“Redis缓存集成”部分)。
缓存的主要接口是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的缓存。缓存名称是大小写敏感的,意味着“MyCache”和“MYCACHE”是不同的缓存。
不要在构造函数中使用GetCache。如果类不是单例的话,缓存可能会被释放。
ICacheManager.GetCaChe方法返回ICache。缓存是单例的(每个缓存名称)。首次需要的时候创建,然后每次都返回同样的缓存对象。所以,我们可以在不同的类(客户端)使用相同的名字共享同样的缓存。
在实例代码中,我们看到了ICache.Get方法的简单使用。它有两个参数:
- Key:缓存中一个项的唯一字符串键。
- factory:如果指定的键没有项目时调用。工厂方法应该创建并返回真实的项。如果指定的键在缓存中存在的话就不会调用这个方法。
ICache接口也有如GetOrDefault,Set,Remove和Clear这样的方法。同样也有这些方法的异步版本。
ICache接口使用string类型作为键,object作为值。ITypeCache是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基类。如果我们通过Ids获取到实体并且想通过Id缓存实体而不是经常从数据库查询,我们就可以使用这个基类。假如我们有一个Person实体,如下:
public class Person : Entity { public string Name { get; set; } public int Age { get; set; } }
假如我们知道Id并想以此获取people的Name。首先,我们创建一个类储存缓存项:
[AutoMapFrom(typeof(Person))] public class PersonCacheItem { public string Name { get; set; } }
不应该直接在缓存中直接存储实体,因为缓存可能需要序列化缓存的对象,实体可能不能被序列化(尤其是有导航属性时)。这就是我们在缓存中定义一个简单类(像DTO)存储数据的原因。因为我们想使用AutoMapper自动将Person实体转换为PersonCahcedItem,所以在类上添加了AutoMapFrom特性。如果我们不使用AutoMapper,那需要重写EntityCache类的MapToCacheItem方法来实现自动转换或映射。
我们希望定义一个缓存类的接口,虽然这不是必须的:
public interface IPersonCache : IEntityCache<PersonCacheItem> { }
最后,我们可以创建缓存类缓存Person实体了:
public class PersonCache : EntityCache<Person, PersonCacheItem>, IPersonCache, ITransientDependency { public PersonCache(ICacheManager cacheManager, IRepository<Person> repository) : base(cacheManager, repository) { } }
就这样,我们的person缓存可以使用了。Cache类可以是临时的(如本例)也可以是单例的。这并不意味着缓存数据是临时的。在应用中,它总是全局缓存并且访问是线程安全的。
现在,不管何时我们需要person的Name时,我们可以通过person`s 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属性。
- 在首次调用时,从仓储(从数据库)获取实体。然后在之后的调用从缓存获取。
- 如果实体被更新或删除,会自动使缓存实体无效。然后,在下次调用时会从数据库中重新获取。
- 使用IObjectMapper映射实体和缓存项。IObjectMapper通过AutoMapper模块实现。所以,如果使用的话需要AutoMapper模块。可以重写MapToCacheItem方法手动映射实体和缓存项。
- 使用缓存类FullName作为缓存名称。可以通过向基类构造函数传递缓存名称来改变它。
- 线程安全。
如果需要更复杂的缓存,可以扩展EntityCache或创建自己的解决方案。
默认的缓存管理器使用内存缓存。所以,如果有多于一个并发网络服务器运行同样的应用,将会成为一个问题。在这种情况下,需要使用一个分布式/中央缓存服务器。可以很简单的使用Redis作为缓存服务器。
首先,需要在应用(例如,可以安装在Web工程)中安装Abp.RedisCache nuget包。然后,需要需要给AbpRedisCacheModule添加DependsOn特性,并在模块的PreInitialize方法中调用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"/>
在同一个服务器中,不同的数据库ids用来创建不同的关键空间(隔离缓存)。
UseRedis方法有个重载,可以用来直接设置选项值(在配置文件中重写值)。
参见Redis文档获取关于Redis及配置的更多信息。
注意:Redis服务器应该安装并运行,以便在ABP中使用Redis缓存。