缓存

    Volo.Abp.Caching是缓存系统的核心包. 可以使用 的add-package命令将其安装到项目中:

    你需要在包含 csproj 文件的文件夹中的命令行终端上运行此命令(请参阅 其他选项 安装).

    使用方式

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

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

    有关更多信息, 参见 .

    IDistributedCache<TCacheItem> 接口

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

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

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

    示例: 在缓存中存储图书名称和价格

    1. namespace MyProject
    2. {
    3. public class BookCacheItem
    4. {
    5. public string Name { get; set; }
    6. public float Price { get; set; }
    7. }
    8. }
    • 示例服务代码中的 GetOrAddAsync() 方法从缓存中获取图书项. GetOrAddAsync是ABP框架在 ASP.NET Core 分布式缓存方法中添增的附加方法.
    • 如果没有在缓存中找到图书,它会调用工厂方法 (本示例中是 GetBookFromDatabaseAsync)从原始数据源中获取图书项.
    • GetOrAddAsync 有一个可选参数 DistributedCacheEntryOptions , 可用于设置缓存的生命周期.

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

    接口默认了缓存键string 类型 (如果你的键不是string类型需要进行手动类型转换). 但当缓存键的类型不是string时, 可以使用IDistributedCache<TCacheItem, TCacheKey>.

    示例: 在缓存中存储图书名称和价格

    示例缓存项

    1. using Volo.Abp.Caching;
    2. namespace MyProject
    3. {
    4. [CacheName("Books")]
    5. public class BookCacheItem
    6. {
    7. public string Name { get; set; }
    8. public float Price { get; set; }
    9. }
    10. }
    • 在本例中使用CacheName特性给BookCacheItem类设置缓存名称.

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

    • 我们采用了 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. }

    用法示例:

    AbpDistributedCacheOptions

    AbpDistributedCacheOptions 是配置缓存的主要.

    示例:为应用程序设置缓存键前缀

    1. {
    2. options.KeyPrefix = "MyApp1";
    3. });

    可用选项

    • HideErrors (bool, 默认: true): 启用/禁用隐藏从缓存服务器写入/读取值时的错误.
    • KeyPrefix (string, 默认: null): 如果你的缓存服务器由多个应用程序共同使用, 则可以为应用程序的缓存键设置一个前缀. 在这种情况下, 不同的应用程序不能覆盖彼此的缓存内容.
    • GlobalCacheEntryOptions (DistributedCacheEntryOptions): 用于设置保存缓内容却没有指定选项时, 默认的分布式缓存选项 (例如 AbsoluteExpirationSlidingExpiration). SlidingExpiration的默认值设置为20分钟.

    错误处理

    当为你的对象设计缓存时, 通常会首先尝试从缓存中获取值. 如果在缓存中找不到该值, 则从来源查询对象. 它可能在数据库中, 或者可能需要通过HTTP调用远程服务器.

    在大多数情况下, 你希望容忍缓存错误; 如果缓存服务器出现错误, 也不希望取消该操作. 相反, 你可以默默地隐藏(并记录)错误并从来源查询. 这就是ABP框架默认的功能.

    ABP的分布式缓存 异常处理, 默认记录并隐藏错误. 有一个全局修改该功能的选项(参见下面的选项内容).

    所有的IDistributedCache<TCacheItem> (和 IDistributedCache<TCacheItem, TCacheKey>)方法都有一个可选的参数hideErrors, 默认值为null. 如果此参数设置为null, 则全局生效, 否则你可以选择单个方法调用时隐藏或者抛出异常.

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

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

    高级主题

    分布式缓存服务提供了一个有趣的功能. 假设你已经更新了数据库中某本书的价格, 然后将新价格设置到缓存中, 以便以后使用缓存的值. 如果设置缓存后出现异常, 并且更新图书价格的事务被回滚了, 该怎么办?在这种情况下, 缓存值是错误的.

    IDistributedCache<..>方法提供一个可选参数, considerUow, 默认为false. 如果将其设置为true, 则你对缓存所做的更改不会应用于真正的缓存存储, 而是与当前的工作单元关联. 你将获得在同一工作单元中设置的缓存值, 但仅当前工作单元成功时更改才会生效.

    IDistributedCacheSerializer

    IDistributedCacheSerializer服务用于序列化和反序列化缓存内容. 默认实现是Utf8JsonDistributedCacheSerializer类, 它使用IJsonSerializer服务将对象转换为JSON, 反之亦然. 然后, 它使用UTC8编码将JSON字符串转换为分布式缓存接受的字节数组.

    如果你想实现自己的序列化逻辑, 可以自己实现并 此服务.