缓存
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
特性以禁用此选项. - 允许为每个应用程序定义 全局缓存键前缀 , 不同的应用程序可以在共享的分布式缓存中拥有自己的隔离池.
- 它可以在任何可能绕过缓存的情况下 容忍错误 的发生. 这在缓存服务器出现临时问题时非常有用.
示例: 在缓存中存储图书名称和价格
namespace MyProject
{
public class BookCacheItem
{
public string Name { get; set; }
public float Price { get; set; }
}
}
- 示例服务代码中的
GetOrAddAsync()
方法从缓存中获取图书项.GetOrAddAsync
是ABP框架在 ASP.NET Core 分布式缓存方法中添增的附加方法. - 如果没有在缓存中找到图书,它会调用工厂方法 (本示例中是
GetBookFromDatabaseAsync
)从原始数据源中获取图书项. GetOrAddAsync
有一个可选参数DistributedCacheEntryOptions
, 可用于设置缓存的生命周期.
IDistributedCache<BookCacheItem>
与ASP.NET Core的IDistributedCache
接口拥有相同的方法, 你可以参考 ASP.NET Core文档.
接口默认了缓存键是 string
类型 (如果你的键不是string类型需要进行手动类型转换). 但当缓存键的类型不是string
时, 可以使用IDistributedCache<TCacheItem, TCacheKey>
.
示例: 在缓存中存储图书名称和价格
示例缓存项
using Volo.Abp.Caching;
namespace MyProject
{
[CacheName("Books")]
public class BookCacheItem
{
public string Name { get; set; }
public float Price { get; set; }
}
}
- 在本例中使用
CacheName
特性给BookCacheItem
类设置缓存名称.
你可以注入 IDistributedCache<BookCacheItem, Guid>
服务用于 get/set BookCacheItem
对象.
- 我们采用了
Guid
做为键, 在_cache_GetOrAddAsync()
方法中传入Guid
类型的bookid.
复杂类型的缓存键
IDistributedCache<TCacheItem, TCacheKey>
在内部使用键对象的 ToString()
方法转换类型为string. 如果你的将复杂对象做为缓存键,那么需要重写类的 ToString
方法.
举例一个作为缓存键的类:
public class UserInOrganizationCacheKey
{
public Guid UserId { get; set; }
public Guid OrganizationId { get; set; }
//构建缓存key
public override string ToString()
{
return $"{UserId}_{OrganizationId}";
}
}
用法示例:
AbpDistributedCacheOptions
AbpDistributedCacheOptions
是配置缓存的主要.
示例:为应用程序设置缓存键前缀
{
options.KeyPrefix = "MyApp1";
});
可用选项
HideErrors
(bool
, 默认:true
): 启用/禁用隐藏从缓存服务器写入/读取值时的错误.KeyPrefix
(string
, 默认:null
): 如果你的缓存服务器由多个应用程序共同使用, 则可以为应用程序的缓存键设置一个前缀. 在这种情况下, 不同的应用程序不能覆盖彼此的缓存内容.GlobalCacheEntryOptions
(DistributedCacheEntryOptions
): 用于设置保存缓内容却没有指定选项时, 默认的分布式缓存选项 (例如AbsoluteExpiration
和SlidingExpiration
).SlidingExpiration
的默认值设置为20分钟.
错误处理
当为你的对象设计缓存时, 通常会首先尝试从缓存中获取值. 如果在缓存中找不到该值, 则从来源查询对象. 它可能在数据库中, 或者可能需要通过HTTP调用远程服务器.
在大多数情况下, 你希望容忍缓存错误; 如果缓存服务器出现错误, 也不希望取消该操作. 相反, 你可以默默地隐藏(并记录)错误并从来源查询. 这就是ABP框架默认的功能.
ABP的分布式缓存 异常处理, 默认记录并隐藏错误. 有一个全局修改该功能的选项(参见下面的选项内容).
所有的IDistributedCache<TCacheItem>
(和 IDistributedCache<TCacheItem, TCacheKey>
)方法都有一个可选的参数hideErrors
, 默认值为null
. 如果此参数设置为null
, 则全局生效, 否则你可以选择单个方法调用时隐藏或者抛出异常.
ABP的分布式缓存接口定义了以下批量操作方法,当你需要在一个方法中调用多次缓存操作时,这些方法可以提高性能
SetManyAsync
和SetMany
方法可以用来向缓存中设置多个值.GetManyAsync
和GetMany
方法可以用来从缓存中获取多个值.GetOrAddManyAsync
和GetOrAddMany
方法可以用来从缓存中获取并添加缺少的值.RefreshManyAsync
和RefreshMany
方法可以来用重置多个值的滚动过期时间.RemoveManyAsync
和RemoveMany
方法可以用来从缓存中删除多个值.
高级主题
分布式缓存服务提供了一个有趣的功能. 假设你已经更新了数据库中某本书的价格, 然后将新价格设置到缓存中, 以便以后使用缓存的值. 如果设置缓存后出现异常, 并且更新图书价格的事务被回滚了, 该怎么办?在这种情况下, 缓存值是错误的.
IDistributedCache<..>
方法提供一个可选参数, considerUow
, 默认为false
. 如果将其设置为true
, 则你对缓存所做的更改不会应用于真正的缓存存储, 而是与当前的工作单元关联. 你将获得在同一工作单元中设置的缓存值, 但仅当前工作单元成功时更改才会生效.
IDistributedCacheSerializer
IDistributedCacheSerializer
服务用于序列化和反序列化缓存内容. 默认实现是Utf8JsonDistributedCacheSerializer
类, 它使用IJsonSerializer
服务将对象转换为JSON, 反之亦然. 然后, 它使用UTC8编码将JSON字符串转换为分布式缓存接受的字节数组.
如果你想实现自己的序列化逻辑, 可以自己实现并 此服务.