缓存

介绍

  ABP为缓存提供了一个抽象接口,它内部使用这个缓存抽象。默认使用MemoryCache实现,但可以换用其他的缓存提供者实现。例如Abp.RedisCache包实现了在Redis中实现缓存。(参见下面的“Redis缓存集成”部分)。

ICacheManager

  缓存的主要接口是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方法

  不要在构造函数中使用GetCache。如果类不是单例的话,缓存可能会被释放。

ICache

  ICacheManager.GetCaChe方法返回ICache。缓存是单例的(每个缓存名称)。首次需要的时候创建,然后每次都返回同样的缓存对象。所以,我们可以在不同的类(客户端)使用相同的名字共享同样的缓存。

  在实例代码中,我们看到了ICache.Get方法的简单使用。它有两个参数:

  • Key:缓存中一个项的唯一字符串键。
  • factory:如果指定的键没有项目时调用。工厂方法应该创建并返回真实的项。如果指定的键在缓存中存在的话就不会调用这个方法。

  ICache接口也有如GetOrDefault,Set,Remove和Clear这样的方法。同样也有这些方法的异步版本。

ITypedCache

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();
}); //Configuration for a specific cache
Configuration.Caching.Configure("MyCache", cache =>
{
cache.DefaultSlidingExpireTime = TimeSpan.FromHours();
});

  这段代码需要放在模块的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缓存集成

  默认的缓存管理器使用内存缓存。所以,如果有多于一个并发网络服务器运行同样的应用,将会成为一个问题。在这种情况下,需要使用一个分布式/中央缓存服务器。可以很简单的使用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=""/>

  在同一个服务器中,不同的数据库ids用来创建不同的关键空间(隔离缓存)。

  UseRedis方法有个重载,可以用来直接设置选项值(在配置文件中重写值)。

  参见Redis文档获取关于Redis及配置的更多信息。

  注意:Redis服务器应该安装并运行,以便在ABP中使用Redis缓存。

返回主目录

ABP官方文档翻译 2.3 缓存的更多相关文章

  1. ABP官方文档翻译 10.1 ABP Nuget包

    ABP Nuget包 Packages Abp Abp.AspNetCore Abp.Web.Common Abp.Web Abp.Web.Mvc Abp.Web.Api Abp.Web.Api.OD ...

  2. 0.0 ABP官方文档翻译目录

    一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...

  3. ABP官方文档翻译 2.5 设置管理

    设置管理 介绍 关于 ISettingStore 定义设置 设置范围 重写设置定义 获取设置值 服务端 客户端 更改设置 关于缓存 介绍 每个应用都需要存储设置,并且在应用的某些地方需要使用这些设置. ...

  4. ABP官方文档翻译 6.2.1 ASP.NET Core集成

    ASP.NET Core 介绍 迁移到ASP.NET Core? 启动模板 配置 启动类 模块配置 控制器 应用服务作为控制器 过滤器 授权过滤器 审计Action过滤器 校验过滤器 工作单元Acti ...

  5. ABP官方文档翻译 5.1 Web API控制器

    ASP.NET Web API控制器 介绍 AbpApiController基类 本地化 其他 过滤器 审计日志 授权 反伪造过滤器 工作单元 结果包装和异常处理 结果缓存 校验 模型绑定器 介绍 A ...

  6. ABP官方文档翻译 0.0 ABP官方文档翻译目录

    一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...

  7. ABP官方文档翻译 1.5 多租户

    多租户 什么是多租户? 数据库和部署架构 多部署-多数据库 单部署-多数据库 单部署-单数据库 单部署-混合数据库 多部署-单/多/混合数据库 ABP的多租户 启用多租户 租主和租户 会话 决定当前租 ...

  8. ABP官方文档翻译 9.3 NHibernate集成

    NHibernate集成 Nuget包 配置 实体映射 仓储 默认实现 自定义仓储 应用程序特定基础仓储类 ABP可以使用任何ORM框架,它内置集成NHibernate.此文档将讲解ABP如何使用NH ...

  9. ABP官方文档翻译 9.2 Entity Framework Core

    Entity Framework Core 介绍 DbContext 配置 在Startup类中 在模块PreInitialize方法中 仓储 默认仓储 自定义仓储 应用程序特定基础仓储类 自定义仓储 ...

随机推荐

  1. Vijos P1035 贪婪的送礼者【模拟】

    贪婪的送礼者 描述 对于一群要互送礼物的朋友,你要确定每个人送出的礼物比收到的多多少. 在这一个问题中,每个人都准备了一些钱来送礼物,而这些钱将会被平均分给那些将收到他的礼物的人. 然而,在任何一群朋 ...

  2. JAVA爬虫实践(实践三:爬虫框架webMagic和csdnBlog爬虫)

    WebMagic WebMagic是一个简单灵活的Java爬虫框架.基于WebMagic,你可以快速开发出一个高效.易维护的爬虫. 采用HttpClient可以实现定向的爬虫,也可以自己编写算法逻辑来 ...

  3. Proxy 那点事儿

    ---恢复内容开始--- 尊重原创:https://my.oschina.net/huangyong/blog/159788 Proxy,也就是"代理"了.意思就是,你不用去做,别 ...

  4. i++是否原子操作?并解释为什么?

    都不是原子操作.理由: 1.i++分为三个阶段: 内存到寄存器寄存器自增写回内存这三个阶段中间都可以被中断分离开.  2.++i首先要看编译器是怎么编译的, 某些编译器比如VC在非优化版本中会编译为以 ...

  5. JQeury添加和删除class内部实现代码(简化版)

    下面是JQuery对元素class操作的简单实现,请看代码: 添加class: //增加class function addClass(elem,value) { var classes, cur, ...

  6. <input type="text">和<textarea>的区别

    在我们开发时经常需要用到输入框,通常解决办法就是<input type="text">和<textarea>,那么这两个标签有什么区别呢?  一:<i ...

  7. Vue.js 1.x 和 2.x 实例的生命周期

    在Vue.js中,在实例化Vue之前,它们都是以HTML的文本形式存在文本编辑器中.当实例化后将经历创建.编译.销毁三个主要阶段. 以下是Vue.js 1.x  实例的生命周期图示: Vue.js 1 ...

  8. 二维数组模拟实现酒店管理系统-java

    业务分析 1.需要一个房间类,包含房间的属性,比如房间编号.房间类型.是否占用. 2.需要一个旅馆类,旅馆有房间,提供的方法需要有 预订房间.打印房间信息.初始化房间.退房. 3.测试类,测试预订房间 ...

  9. vue.js打包后,接口安全问题

    后面有一位朋友回答后台origin判断,但另一个朋友说可以通过代理请求就绕过跨域. 想想也对,代理的话origin就无效了,页面还是可以跑起来. 不知道有没有人想过这个问题,还是我想的方向有误,请各位 ...

  10. 织梦5.7DEDECMS标签大全

    1.关键描述调用标签: 2.路径调用标签: {dede:field name='templeturl'/} {dede:global.cfg_templets_skin/} 3.网站标题调用标签: d ...