## 2.3 ABP公共结构 - 缓存管理
### 2.3.1 简介
ABP提供了一个抽象的缓存基类(CacheBase),并且在内部使用 MemoryCache来实现了这个抽象类的所有功能,当然你也能够用其他的缓存提供器来实现它。(例如:Redis 或者 Memcached)
### 2.3.2 ICacheMananger
ABP对外提供了一个缓存接口ICacheMananger。我们通过构造函数注入这个接口来获取缓存。示例如下:
```csharp
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方法,如果的你的类是一个瞬时对象(transient)这可能导致你的缓存被dispose掉。
### 2.3.3 ICache
ICacheManager.GetCache方法返回了一个ICache对象。每一个缓存都是基于名称单例存在的。只有首次访问时才会被创建,以后你每次用相同的名称去获取的缓存都是相同的。所以我们可以在不同的类中使用相同的名称来共享相同的缓存。
在示例代码中,我们简单的使用了ICache.Get方法,它有两个参数:
* key : 要获取的缓存项的唯一标识符
* factory:如果根据给定的key获取到的缓存项为空,那么factory将会创建一个标识符为key的缓存,并且返回该缓存
ICache接口还有其它方法,例如:GetOrDefault,Set,Remove和Clear。当然也有这些方法的异步(async)版本。
### 2.3.4 ITypedCache
ICache接口用key(字符串类型)来获取缓存value(object类型)。ITypedCache为ICahe提供了一个类型安全的包装;为了使类型安全转换(ICache到ITypedCache),我们可以用扩展方法AsTyped,而不需要写其它强制类型转换的代码,如下所示:
``` csharp
ITypedCache<int, Item> myCache = _cacheManager.GetCache("MyCache").AsTyped<int, Item>();
```
### 2.3.5 Configuration
缓存的过期时间默认是60分钟。如果你想改变所有的缓存或者指定的缓存来的默认过期时间,你可以这样做,实现如下:
```csharp
//对所有缓存的配置
Configuration.Caching.ConfigureAll(cache =>
{
cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2);
});
//对指定缓存的配置
Configuration.Caching.Configure("MyCache", cache =>
{
cache.DefaultSlidingExpireTime = TimeSpan.FromHours(8);
});
```
这段代码你应该放在模块(module)的PreInitialize方法中。如上所示:MyCache将会在8小时后过期,而其他的缓存将在2小时后过期。
这些配置将会在首次创建缓存的时候生效。配置不仅仅局限于DefaultSlidingExpireTime,你可以利用ICache接口中的属性获取方法来自由的配置并且初始化它们。
### 2.3.6 Entity Caching
ABP的缓存系统是以通用为目的,它有一个 **EntityCache** 基类,如果你需要的话,这个基类可以帮助你缓存实体。使用这个基类,我们可以通过ID取得实体,并且我们通过ID来缓存实体,这样以后就不需要频繁的查询数据库去取得实体。假设我们有个Person实体,像下面一样:
```csharp
public class Person : Entity
{
public string Name { get; set; }
public int Age { get; set; }
}
```
并且,假设我们通过该实体的Id,需要频繁调用取得Person实体的Name。首先,我们应该创建一个类来存储 **cache items**:
```csharp
[AutoMapFrom(typeof(Person))]
public class PersonCacheItem
{
public string Name { get; set; }
}
```
我们 **不应该直接存储实体到缓存中** 因为缓存的时候需要序列化缓存对象而实体可能不能被序列化(尤其是实体的导航属性)。这就是为什么我们定义了一个简单的像DTO的类来存储数据到缓存中。我们添加了 **AutoMapFrom** 特性,这是因为我们想使用 **AutoMapper** 来自动的转换 **Person** 实体为 **PersonCacheItem** 对象。如果我们不使用 AutoMapper,那么我们应该重写 **EntityCache** 类的 **MapToCacheItem** 方法手动转换/映射它。
然而这不是必须的,我们可能想定义一个接口为缓存类:
```csharp
public interface IPersonCache : IEntityCache<PersonCacheItem>
{
}
```
最后,我们可以创建缓存类来缓存Person实体:
```csharp
public class PersonCache : EntityCache<Person, PersonCacheItem>, IPersonCache, ITransientDependency
{
public PersonCache(ICacheManager cacheManager, IRepository<Person> repository)
: base(cacheManager, repository)
{
}
}
```
这样就OK了,我们的person缓存已经准备好可以使用了。缓存类可以使瞬时(如同这个例子)或者是单例。这不是说缓存数据是瞬态的。在你的应用程序中它一直是全局缓存并且是线程安全的。
现在,无论在什么地方我们需要取得Person的Name,我们可以通过Person的Id从缓存中取得它。如下所示:
```csharp
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;
}
}
```
我们很容易的[注入](225827) **IPersonCache** 接口,通过该接口取得缓存项和Name属性。
#### 那么EntityCache是怎么工作的?
+ 在首次调用的时候我们通过仓储从数据库中取得实体。那么随后的调用都是从缓存中取得。
+ 如果实体被更新或者删除,它会自动的无效实体。因此,它会在下次调用的时候重新从数据库中检索数据。
+ 使用 **IObjectMapper** 接口来映射实体到缓存项。**IObjectMapper** 接口在 **AutoMapper** 中被实现。所以,如果你使用了自动映射,那么就需要 **AutoMapper模块**。你可以重写 **MapToCacheItem** 方法手动映射它到缓存项。
+ 使用缓存类的FullName作为缓存的Name,你可以通过传入的缓存名到基类的构造函数来改变它。
+ 它是线程安全的。
如果你有更复杂的缓存需求,那么你需要扩展 **EntityCache** 类或者创建你自己的解决方案。
#### Redis Cache 集成
默认Cache Mananger是使用 **in-memory** 来缓存。所以,这可能会成为一个问题,如果有多个并发的Web服务运行在同一个应用中。在这种情况下,你可能想要一个分布式/中央缓存服务器。那么,你可以使用Redis来作为你的缓存服务。
首先,你需要安装 [Abp.RedisCache](https://www.nuget.org/packages/Abp.RedisCache) nuget package 到你的项目中(你可以安装它到你的Web项目)。那么你需要为 **AbpRedisCacheModule** 添加 **DependsOn** 特性,并且在你模块的 **PreInitialize** 方法中调用 **UseRedis** 扩展方法,如下所示:
```csharp
//...其他名称空间的引用
using Abp.Runtime.Caching.Redis;
namespace MyProject.AbpZeroTemplate.Web
{
[DependsOn(
//...其他依赖模块
typeof(AbpRedisCacheModule))]
public class MyProjectWebModule : AbpModule
{
public override void PreInitialize()
{
//...其他配置
Configuration.Caching.UseRedis();
}
//...其他代码
}
}
```
Abp.RedisCache package 使用 **“localhost”** 作为默认的 **连接字符串**。你可以添加连接字符串到你的配置文件中来覆盖它。如:
```html
<add name="Abp.Redis.Cache" connectionString="localhost"/>
```
你也可以添加配置到appSettings来设置Redis数据库的Id。如:
```html
<add key="Abp.Redis.Cache.DatabaseId" value="2"/>
```
在同一个服务器上使用不同的数据库Id是非常有用的这可以创建不同的Key Spaces(隔离缓存)。
**UseRedis** 有一个重载方法,你可以通过这个方法来传入配置参数,这可以覆盖掉配置文件中的配置。
关于Redis的其他配置可以查看 [Redis文档](http://redis.io/documentation)。
当然译者也简单的对[StackExchange.Redis](https://github.com/carldai0106/StackExchange.Redis-Chinese-Doc)进行了翻译,吐槽一下这个文档的作者可能真的是一个真正的程序员,你懂的。
>注意:在ABP中使用Redis缓存你需要安装Redis服务并使其运行。
- 文章&教程
- Abp
- 1.1ABP总体介绍-入门介绍
- 1.2ABP总体介绍-多层架构体系
- 1.3ABP总体介绍-模块系统
- 1.4ABP总体介绍-启动配置
- 1.5ABP总体介绍-多租户
- 1.6ABP总体介绍-集成OWIN
- 2.1ABP公共结构-依赖注入
- 2.2ABP公共结构-会话管理
- 2.3ABP公共结构-缓存管理
- 2.4ABP公共结构-日志管理
- 2.5ABP公共结构-设置管理
- 2.6ABP公共结构-时区设置
- 3.1ABP领域层-实体
- 3.2ABP领域层-值对象
- 3.3ABP领域层-仓储
- 3.4ABP领域层-领域服务
- 3.5ABP领域层-工作单元
- 3.6ABP领域层-领域事件
- 3.7ABP领域层-数据过滤器
- 4.1ABP应用层-应用服务
- 4.2ABP应用层-数据传输对象
- 4.3ABP应用层-数据传输对象验证
- 4.4ABP应用层-权限认证
- 4.5ABP应用层-功能管理
- 4.6ABP应用层-审计日志
- 5.1ABP分布式服务-ASP.NET WebApi
- 5.2ABP分布式服务-动态WebApi层
- 5.3ABP分布式服务-集成OData
- 5.4ABP分布式服务-集成SwaggerUI
- 6.1ABP表现层-Mvc控制器
- 6.2ABP表现层-Mvc视图
- 6.3ABP表现层-本地化
- 6.4ABP表现层-导航栏
- 6.5ABP表现层-异常处理
- 6.6.2-AJAX
- 6.6.3-Notification
- 6.6.4-Message
- 6.6.5-UIBlockBusy
- 6.6.6-EventBus
- 6.6.7-Logging
- 6.6.8-OtherUtilities
- 6.6ABP表现层-JavascriptAPI
- 6.7ABP表现层-嵌入资源文件
- 6.8ASP.NET-Core
- 6.9CSRF和XSRF保护
- 7.1ABP后台服务-后台作业和工人
- 7.2ABP后台服务-集成Hangfire
- 8.1ABP实时服务-通知系统
- 8.2ABP实时服务-集成SignalR
- 9.1ABP基础设施层-集成Entity Framework
- 9.2ABP基础设施层-集成NHibernate
- 9.3ABP基础设施层-集成Entity Framework Core
- abp合并版160929
- md文档排版规范
- AbpZero
- 1.1ABPZero-概述
- 1.2ABPZero-安装
- 1.3ABPZero-启动模板
- 1.4ABPZero-启动模板Core
- 2.1ABPZero-多租户管理
- 2.2ABPZero-版本管理
- 2.4ABPZero-组织单位管理