本文将讲解如何在ABP中使用Redis Cache以及使用过程中遇到的各种问题。下面就直接讲解使用步骤,Redis环境的搭建请直接网上搜索。

使用步骤:

一、ABP环境搭建

  1. 到http://www.aspnetboilerplate.com/Templates下载一个ABP项目的模板,项目 类型选择Angularjs+EntityFramework,项目名称为“UsingRedisInAbp”
  2. 生成数据库,并初始化基本数据。在包管理器的控制台上运行Updata-Database命令,运行时需要注意,默认项目要选中“UsingRedisInAbp.EntityFramework”,启动项目要设置为“UsingRedisInAbp.Web”
  3. 在nuget里面添加对”Abp.RedisCache”的引用,我引用的0.7.8.1版本

二、替换默认的缓存管理器

修改UsingRedisInAbpApplicationModule类的代码,主要是修改默认缓存管理器和Redis的连接字符串,修改后的完整代码如下:

[DependsOn(typeof(UsingRedisInAbpCoreModule), typeof(AbpAutoMapperModule))]public class UsingRedisInAbpApplicationModule : AbpModule{public override void PreInitialize(){           base.PreInitialize();IocManager.Register<ICacheManager, AbpRedisCacheManager>();//如果Redis在本机,并且使用的默认端口,下面的代码可以不要//Configuration.Modules.AbpRedisCacheModule().ConnectionStringKey = "KeyName";
        }public override void Initialize(){IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());}}

View Code

三、缓存的使用

现在我们编译一下项目,编译通过后我们按F5运行,如果看到如下界面,表示运行成功了

下面我们看看Redis里是否有相关的缓存信息,用Redis Desktop Manager连接Redis,可以看到如下信息,说明信息已经缓存成功了

四、自定义缓存信息的读取与设置

为了演示方便,我们以读取文章信息为例来说明,文章信息只包含一个”Title”字段.

  1. 在.Core项目里添加文章类,然后在UsingRedisInAbpDbContext类里添加一个Articles属性
  2. 使用Add-Migration添加信息,然后更新数据库
  3. 在.Application项目里添加Caching文件夹,并在里面添加读取和设置缓存的通用接口和实现类,实现类CacheService.cs的代码如下:

public class CacheService : ICacheService,ISingletonDependency{public ICacheManager CacheManager { get; set; }public TValue GetCachedEntity<TKey, TValue>(TKey key) where TValue : class,IEntity<TKey>{var cache = CacheManager.GetCache<TKey, TValue>(typeof(TValue).Name);var item = cache.Get(key, () =>{var repository = IocManager.Instance.Resolve<IRepository<TValue, TKey>>();var entity = repository.FirstOrDefault(key);if (entity == null){throw new UserFriendlyException(string.Format("读取的信息不存在,Key:{0}",key));}return entity;});return item;}public TValue GetCachedEntity<TValue>(int key) where TValue : class, IEntity<int>{return GetCachedEntity<int, TValue>(key);}public void Set<TKey, TValue>(TKey key, TValue value, TimeSpan? slidingExpireTime = null){var cache = CacheManager.GetCache<TKey, TValue>(typeof(TValue).Name);cache.Set(key, value, slidingExpireTime);}}

View Code

从缓存读取信息的逻辑为:首先从缓存里读取信息,如果未读取到,则从数据库读取对应信息,并且将信息保存到缓存中

  4.修改前端相关代码,为了方便测试,直接在home.js和home.cshtml里添加访问缓存信息的代码,home.js的代码如下

home.cs

(function() {var controllerId = 'app.views.home';angular.module('app').controller(controllerId, ['$scope', 'abp.services.app.cacheTest', function ($scope,service) {var vm = this;//Home logic...vm.article = {};vm.title = "";vm.id = 0;vm.createArticle = function() {service.createArticle({ title: vm.title }).success(function(result) {abp.notify.success("文章创建成功");});};vm.getArticle = function() {service.getArticle({ id:vm.id}).success(function (result) {vm.article = result;});};}]);
})();

View Code

home.cshtml

<div ng-controller="app.views.home as vm"><h1>@L("WellcomeMessage")</h1><div class="row"><div class=" well well-sm"><div class="row"><div class="col-md-6"><input type="text" ng-model="vm.id" class="form-control" placeholder="id" required="" maxlength="32"></div><div class="col-md-6"><button type="button" ng-click="vm.getArticle()" class="btn btn-primary"><i class="fa fa-sign-in"></i> 读取缓存信息</button></div>                                </div><div><div>{{vm.article.id}}</div><div>{{vm.article.title}}</div></div></div><div class=" well well-sm"><div class="row"><div class="col-md-6"><input ng-model="vm.title" type="text" class="form-control" placeholder="文章标题" required="" maxlength="32"></div><div class="col-md-6"><button type="button" ng-click="vm.createArticle()" class="btn btn-primary"><i class="fa fa-sign-in"></i> 新建文章</button></div></div></div></div>
</div>

View Code

现在访问页面,看看能否正常添加信息与访问缓存里的信息,我们打开首页,可以看到能够正常添加与访问缓存的信息

  5.现在缓存能够正常的设置与读取了,但是有一个很严重的问题,那就是重启web服务后,无法正常加载缓存,必须要清空缓存,网站才能正常运行,报错信息如下:

Unable to find assembly 'EntityFrameworkDynamicProxies-Abp.Zero, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

根据错误信息来看,应该是EntityFramework动态生成了实体类的代理类,导致反序列化失败,通过查看ABP的源代码我们可以知道,ABP使用的是BinaryFormatter,动态生成的实体类使用BinaryFormatter是有问题的。要解决这个问题有以下两种方法:

A) 替换ABP的Redis缓存默认实现,不使用BinaryFormatter进行序列化,使用JSON.NET进行序列化

要替换ABP的Redis缓存默认实现修改修改3个地方

  1. 实现一个ICache,可以参考ABP的实现,修改序列化与反序列化的相关代码,序列化与反序列化时需要注意,需要将原始对象包装到RedisCacheItem中,之所以要这样做,是因为反序列化时需要获取原始对象的类型,如果直接反序列化为object对象,有时会直接被反序列化为JObject对象,这时就没法直接转换为原始对象了
  2. 实现一个ICacheManager,可以参考ABP的实现
  3. 将新实现的ICacheManager注册到IOC中

修改后的完整代码如下:

RedisCacheItem.cs

public class RedisCacheItem{public Type Type { get; set; }public string Item { get; set; }}

View Code

RedisCacheManager.cs

public class RedisCacheManager : CacheManagerBase{public RedisCacheManager(IIocManager iocManager, ICachingConfiguration configuration): base(iocManager, configuration){IocManager.RegisterIfNot<RedisCache>(DependencyLifeStyle.Transient);}protected override ICache CreateCacheImplementation(string name){return IocManager.Resolve<RedisCache>(new { name });}}

View Code

RedisCache.cs

public class RedisCache : CacheBase{private readonly ConnectionMultiplexer _connectionMultiplexer;private readonly AbpRedisCacheConfig _config;public IDatabase Database{get{return _connectionMultiplexer.GetDatabase();}}public RedisCache(string name, IAbpRedisConnectionProvider redisConnectionProvider, AbpRedisCacheConfig config): base(name){_config = config;var connectionString = redisConnectionProvider.GetConnectionString(_config.ConnectionStringKey);_connectionMultiplexer = redisConnectionProvider.GetConnection(connectionString);}public override object GetOrDefault(string key){var obj = Database.StringGet(GetLocalizedKey(key));if (obj.HasValue){var item = JsonConvert.DeserializeObject < RedisCacheItem>(obj);return JsonConvert.DeserializeObject(item.Item, item.Type);                }return null;}public override void Set(string key, object value, TimeSpan? slidingExpireTime = null){if (value == null){throw new AbpException("Can not insert null values to the cache!");}var cacheItem = new RedisCacheItem { Type = value.GetType(), Item = JsonConvert.SerializeObject(value) };Database.StringSet(GetLocalizedKey(key),JsonConvert.SerializeObject(cacheItem),slidingExpireTime);}public override void Remove(string key){Database.KeyDelete(GetLocalizedKey(key));}public override void Clear(){Database.KeyDeleteWithPrefix(GetLocalizedKey("*"));}private string GetLocalizedKey(string key){return "n:" + Name + ",c:" + key;}
} 

View Code

UsingRedisInAbpApplicationModule.cs

[DependsOn(typeof(UsingRedisInAbpCoreModule), typeof(AbpAutoMapperModule))]public class UsingRedisInAbpApplicationModule : AbpModule{public override void PreInitialize(){           base.PreInitialize();//IocManager.Register<ICache,RedisCache>();IocManager.Register<ICacheManager, RedisCacheManager>();            //如果Redis在本机,并且使用的默认端口,下面的代码可以不要//Configuration.Modules.AbpRedisCacheModule().ConnectionStringKey = "KeyName";
        }public override void Initialize(){IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());}}

View Code

B)禁用EntityFramework的动态代理实体类生成功能

直接在UsingRedisInAbpDbContext类的构造函数中添加如下代码

Configuration.ProxyCreationEnabled = false;

此时无论是使用第一种方式还是第二种方式,都能够正常的读取和设置缓存了。

两种修改方式比较:

  1. 使用第一种方式时,会序列化与反序列化两次,性能会受到一定的影响
  2. 使用第二种方式时,实体的导航属性延迟加载功能会受到影响

那么有没有一种方式可以实现只序列化一次,实体的导航属性够延迟加载也不受影响呢?如果一定要实现的话,可以这样做,ABP的默认缓存实现不进行修改,只将我们自己的自定义缓存实现换成访问Redis Cache就行。

目前的缓存还无法自动更新,下一篇将实现原始数据增删改后,同步更新缓存的内容。

本文的完整代码下载地址:http://files.cnblogs.com/files/loyldg/UsingRedisInAbp.src.rar

转载于:https://www.cnblogs.com/loyldg/p/using-redis-in-abp-1.html

ABP中使用Redis Cache(1)相关推荐

  1. Abp框架从零开始(基于.Net Core 2.2) 小记(四) Abp中使用Redis

    ABP中的默认缓存功能是基于MemroyCache来实现的.MemroyCache对于复杂的结构和操作.数据同步和持久化来说,是不适用的,所以我们将在系统中集成使用Redis缓存,redis的安装就不 ...

  2. ABP中缓存的使用方法

    在ABP中环境使用非常简单,并且可以选择是否使用 Redis 作为缓存.默认如果没有配置 Redis 则使用内存缓存. 首先注入 ICacheManager 接口 public class SmsMa ...

  3. Wordpress优化:网站用nginx前端缓存+Redis Cache缓存提速网站

    本问转载:https://www.src1024.com/xy/seo/31780.html 新手站长搭建网站一般为了省钱没去选择大厂的云服务器,而小厂的服务器虽然便宜,但是访问速度也很感人,同时如果 ...

  4. django 框架中应用 redis 集群

    1. 在 setting.py 中添加 : CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION' ...

  5. Azure Redis Cache (3) 创建和使用P级别的Redis Cache

    <Windows Azure Platform 系列文章目录> 在笔者之前的文档里面已经说明了,Azure Redis Cache分为三个不同的级别: - 基本,Basic,不包含SLA ...

  6. django 框架中应用 redis 集群(亲测)

    1. 在 setting.py 中添加 : CACHES = {'default': {'BACKEND': 'django_redis.cache.RedisCache','LOCATION': r ...

  7. java+cache使用方法_java相关:Spring boot redis cache的key的使用方法

    java相关:Spring boot redis cache的key的使用方法 发布于 2020-8-16| 复制链接 摘记: 在数据库查询中我们往往会使用增加缓存来提高程序的性能,@Cacheabl ...

  8. 如何在 .NET 中使用 Redis缓存

    译文链接:https://www.infoworld.com/article/3187905/how-to-work-with-redis-cache-in-net.html 缓存是一种状态管理机制, ...

  9. redis 清空缓存_「镜头回放」简直了!spring中清除redis缓存导致应用挂死

    异常场景 springWeb应用一直运行正常,同事最近反应,每次版本更新完毕,刷新缓存,就会导致应用挂死.只有重启redis应用才恢复正常. 项目概况 springWeb项目,常用配置表做了redis ...

最新文章

  1. 福建省2013高职单招计算机类试题,13年福建-高职单招-计算机类试题及答案.doc
  2. Android 中文 API 文档 (45) —— AbsoluteLayout.LayoutParams
  3. java 规范异常的处理_规范-异常处理
  4. httpurlconnect设置中文参数_CNC数控机床三菱系统参数的设置及报警解除
  5. mxnet:mx.sym.BlockGrad理解
  6. java xfire下载_XFIRE教程 PDF 下载
  7. 小创业项目组的管理和实践:快速迭代的思考
  8. 该如何在后期处理中,实现高亮描边的效果?
  9. 【第二组】项目冲刺(Beta版本)第一次每日例会 2017/7/18
  10. java百度云文件上传_关于如何在自己项目集成百度云BCE文件上传STS方案
  11. 数据结构——二叉搜索树的C语言实现
  12. HDU 6625 three arrays 求两个序列异或最小值的排列(一个可以推广的正解
  13. Android NDK开发之旅25 NDK 模仿QQ变声特效
  14. Python是否支持短路?
  15. js自写字符串 append 方法
  16. 使用npm安装vue项目+使用
  17. Spring MVC - 介绍
  18. Python-Leetcode-剑指offer(五月上做题整理)
  19. 2021年连云港高考成绩查询,2021年连云港高考状元是谁分数多少分,历年连云港高考状元名单...
  20. GPRS DTU是什么?其工作原理是什么?

热门文章

  1. QT中中文乱码的解决
  2. JS日历控件优化(增加时分秒)
  3. MySQL 5.5 到MySQL 5.6半同步复制(SSL)
  4. SQL2005恢复只有mdf文件的数据库
  5. L3-016 二叉搜索树的结构 (30 分)-PAT 团体程序设计天梯赛 GPLT
  6. PAT 乙级 1027. 打印沙漏(20) Java版
  7. 【算法】动态规划笔记
  8. 【YII】常用代码积累
  9. oracle 存储过程给另一个用户的权限问题
  10. Oracle之唯一性约束(UNIQUEConstraint)用法详解