缓存

ABP框架扩展了ASP.NET Core的分布式缓存系统.

Volo.Abp.Caching Package

默认情况下启动模板已经安装了这个包,所以大部分情况下你不需要手动安装.

Volo.Abp.Caching是缓存系统的核心包.使用包管理控制台(PMC)安装到项目:

  1. Install-Package Volo.Abp.Caching

然后将 AbpCachingModule 依赖添加到你的模块:

  1. using Volo.Abp.Modularity;
  2. using Volo.Abp.Caching;
  3. namespace MyCompany.MyProject
  4. {
  5. [DependsOn(typeof(AbpCachingModule))]
  6. public class MyModule : AbpModule
  7. {
  8. //...
  9. }
  10. }

IDistributedCache 接口

ASP.NET Core 定义了 IDistributedCache 接口用于 get/set 缓存值 . 但是会有以下问题:

  • 它适用于 byte 数组 而不是 .NET 对象. 因此你需要对缓存的对象进行序列化/反序列化.
  • 它为所有的缓存项提供了 单个 key 池 , 因此 ;
    • 你需要注意键区分 不同类型的对象.
    • 你需要注意不同租户(参见多租户)的缓存项.

IDistributedCache 定义在 Microsoft.Extensions.Caching.Abstractions 包中. 这使它不仅适用于ASP.NET Core应用程序, 也可用于任何类型的程序.

IDistributedCache 接口的默认实现是 MemoryDistributedCache 它使用内存工作. 参见 ASP.NET Core文档 了解如何切换到 Redis 或其他缓存提供程序.

有关更多信息, 参见 ASP.NET Core 分布式缓存文档.

IDistributedCache<TCacheItem> 接口

ABP框架在Volo.Abp.Caching包定义了通用的泛型 IDistributedCache<TCacheItem> 接口. TCacheItem 是存储在缓存中的对象类型.

IDistributedCache<TCacheItem> 接口了上述中的问题;

  • 它在内部 序列化/反序列化 缓存对象. 默认使用 JSON 序列化, 但可以替换依赖注入系统中 IDistributedCacheSerializer 服务的实现来覆盖默认的处理.
  • 它根据缓存中对象类型自动向缓存key添加 缓存名称 前缀. 默认缓存名是缓存对象类的全名(如果你的类名以CacheItem 结尾, 那么CacheItem 会被忽略,不应用到缓存名称上). 你也可以在缓存类上使用 CacheName 设置换缓存的名称.
  • 它自动将当前的租户id添加到缓存键中, 以区分不同租户的缓存项 (只有在你的应用程序是多租户的情况下生效). 在缓存类上应用 IgnoreMultiTenancy attribute, 可以在所有的租户间共享缓存.
  • 允许为每个应用程序定义 全局缓存键前缀 ,不同的应用程序可以在共享的分布式缓存中拥有自己的隔离池.

使用方式

缓存中存储项的示例类:

  1. public class BookCacheItem
  2. {
  3. public string Name { get; set; }
  4. public float Price { get; set; }
  5. }

你可以注入 IDistributedCache<BookCacheItem> 服务用于 get/set BookCacheItem 对象.

使用示例:

  1. public class BookService : ITransientDependency
  2. {
  3. private readonly IDistributedCache<BookCacheItem> _cache;
  4. public BookService(IDistributedCache<BookCacheItem> cache)
  5. {
  6. _cache = cache;
  7. }
  8. public async Task<BookCacheItem> GetAsync(Guid bookId)
  9. {
  10. return await _cache.GetOrAddAsync(
  11. bookId.ToString(), //Cache key
  12. async () => await GetBookFromDatabaseAsync(bookId),
  13. () => new DistributedCacheEntryOptions
  14. {
  15. AbsoluteExpiration = DateTimeOffset.Now.AddHours(1)
  16. }
  17. );
  18. }
  19. private Task<BookCacheItem> GetBookFromDatabaseAsync(Guid bookId)
  20. {
  21. //TODO: get from database
  22. }
  23. }
  • 示例服务代码中的 GetOrAddAsync() 方法从缓存中获取图书项.
  • 如果没有在缓存中找到图书,它会调用工厂方法 (本示例中是 GetBookFromDatabaseAsync)从原始数据源中获取图书项.
  • GetOrAddAsync 有一个可选参数 DistributedCacheEntryOptions , 可用于设置缓存的生命周期.

IDistributedCache<BookCacheItem> 的其他方法与ASP.NET Core的IDistributedCache 接口相同, 你可以参考 ASP.NET Core文档.

IDistributedCache<TCacheItem, TCacheKey> 接口

IDistributedCache<TCacheItem> 接口默认了键是 string 类型 (如果你的键不是string类型需要进行手动类型转换). IDistributedCache<TCacheItem, TCacheKey> 将键的类型泛型化试图简化手动转换的操作.

使用示例

示例缓存项

  1. public class BookCacheItem
  2. {
  3. public string Name { get; set; }
  4. public float Price { get; set; }
  5. }

用法示例 (这里假设你的键类型是 Guid):

  1. public class BookService : ITransientDependency
  2. {
  3. private readonly IDistributedCache<BookCacheItem, Guid> _cache;
  4. public BookService(IDistributedCache<BookCacheItem, Guid> cache)
  5. {
  6. _cache = cache;
  7. }
  8. public async Task<BookCacheItem> GetAsync(Guid bookId)
  9. {
  10. return await _cache.GetOrAddAsync(
  11. bookId, //Guid type used as the cache key
  12. async () => await GetBookFromDatabaseAsync(bookId),
  13. () => new DistributedCacheEntryOptions
  14. {
  15. AbsoluteExpiration = DateTimeOffset.Now.AddHours(1)
  16. }
  17. );
  18. }
  19. private Task<BookCacheItem> GetBookFromDatabaseAsync(Guid bookId)
  20. {
  21. //TODO: get from database
  22. }
  23. }
  • 示例服务中 GetOrAddAsync() 方法获取缓存的图书项.
  • 我们采用了 Guid 做为键,在 _cache_GetOrAddAsync() 方法中传入 Guid 类型的bookid.

IDistributedCache<TCacheItem, TCacheKey> 在内部使用键对象的 ToString() 方法转换类型为string. 如果你的将复杂对象做为键,那么需要重写类的 ToString 方法.

示例:

  1. public class UserInOrganizationCacheKey
  2. {
  3. public Guid UserId { get; set; }
  4. public Guid OrganizationId { get; set; }
  5. //构建缓存key
  6. public override string ToString()
  7. {
  8. return $"{UserId}_{OrganizationId}";
  9. }
  10. }

用法示例:

  1. public class BookService : ITransientDependency
  2. {
  3. private readonly IDistributedCache<UserCacheItem, UserInOrganizationCacheKey> _cache;
  4. public BookService(
  5. IDistributedCache<UserCacheItem, UserInOrganizationCacheKey> cache)
  6. {
  7. _cache = cache;
  8. }
  9. ...
  10. }

批量操作

ABP的分布式缓存接口定义了以下批量操作方法,当你需要在一个方法中调用多次缓存操作时,这些方法可以提高性能

  • SetManyAsyncSetMany 方法可以用来设置多个值.
  • GetManyAsyncGetMany 方法可以用来从缓存中获取多个值.
  • GetOrAddManyAsyncGetOrAddMany 方法可以用来从缓存中获取并添加缺少的值.
  • RefreshManyAsyncRefreshMany 方法可以来用重置多个值的滚动过期时间.
  • RemoveManyAsyncRemoveMany 方法呆以用来删除多个值.

这些不是标准的ASP.NET Core缓存方法, 所以某些提供程序可能不支持. ABP Redis集成包实现了它们. 如果提供程序不支持,会回退到 SetAsyncGetAsync … 方法(循环调用).

DistributedCacheOptions

TODO