本文将讲解如何在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());
}
}

三、缓存的使用

现在我们编译一下项目,编译通过后我们按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);
} }

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

  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;
});
};
}
]);
})();

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>

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

  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; }
}

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 });
}
}

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;
}
}

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());
}
}

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

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

Configuration.ProxyCreationEnabled = false;

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

两种修改方式比较:

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

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

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

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

ABP中使用Redis Cache(1)的更多相关文章

  1. ABP中使用Redis Cache(2)

    上一篇讲解了如何在ABP中使用Redis Cache,虽然能够正常的访问Redis,但是Redis里的信息无法同步更新.本文将讲解如何实现Redis Cache与实体同步更新.要实现数据的同步更新,我 ...

  2. Azure Redis Cache

    将于 2014 年 9 月 1 日停止Azure Shared Cache服务,因此你需要在该日期前迁移到 Azure Redis Cache.Azure Redis Cache包含以下两个层级的产品 ...

  3. Spring Boot 揭秘与实战(二) 数据缓存篇 - Redis Cache

    文章目录 1. Redis Cache 集成 2. 源代码 本文,讲解 Spring Boot 如何集成 Redis Cache,实现缓存. 在阅读「Spring Boot 揭秘与实战(二) 数据缓存 ...

  4. ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十五节--缓存小结与ABP框架项目中 Redis Cache的实现

    返回总目录:ABP+AdminLTE+Bootstrap Table权限管理系统一期 缓存 为什么要用缓存 为什么要用缓存呢,说缓存之前先说使用缓存的优点. 减少寄宿服务器的往返调用(round-tr ...

  5. 缓存与ABP Redis Cache

    缓存与ABP Redis Cache 为什么要用缓存 为什么要用缓存呢,说缓存之前先说使用缓存的优点. 减少寄宿服务器的往返调用(round-trips). 如果缓存在客户端或是代理,将减少对服务器的 ...

  6. 使用abp的 redis cache

    top 使用abp的 redis cache -1. 在微软维护的github项目的release里找到redis的windows版本 64位 大约5M,安装,安装,然后在安装目录找到redis.wi ...

  7. .NET中使用Redis (二)

    很久以前写了一篇文章 .NET中使用Redis 介绍了如何安装Redis服务端,以及如何在.NET中调用Redis读取数据.本文简单介绍如何设计NoSQL数据库,以及如何使用Redis来存储对象. 和 ...

  8. 如何在ASP.NET Core中使用Redis

    注:本文提到的代码示例下载地址> https://code.msdn.microsoft.com/How-to-use-Redis-in-ASPNET-0d826418 Redis是一个开源的内 ...

  9. Azure Redis Cache (1) 入门

    <Windows Azure Platform 系列文章目录> Microsoft Azure Redis Cache基于流行的开源Redis Cache. 1.功能 Redis 是一种高 ...

随机推荐

  1. Java 浅析内部类

    这篇文章主要讲述Java 内部类的相关知识,主要讲解下面的知识点. 内部类的概念 内部类的特点与使用 多种形式内部类 为什么要使用内部类 内部类的概念 内部类是指在一个类的内部定义了另一个类.例如下面 ...

  2. vs code插件记录

    最近一段时间一直在迷恋vs code,使用了一段时间,发现它即精简又快速,安装插件又快又稳定而且插件说明也很详细,是一款继sublime后少得的良心前端编辑器,配合上一些插件可以补全一些不足以更趋向于 ...

  3. 微信蓝牙设备开发教程之获取设备deviceid和二维码(3)

    文章转载地址  http://www.vxzsk.com/87.html  设备授权 调用 设备授权新接口 ,获取deviceid和二维码,然后利用获取到的deviceid更新设备属性(如mac地址, ...

  4. Spring学习记录(十)---使用FactoryBean配置Bean

    之前学了,配置bean可以用普通全类名配置.用工厂方法配置,FactoryBean又是什么呢 有时候配置bean要用到,IOC其他Bean,这时,用FactoryBean配置最合适. FactoryB ...

  5. DataGrid中的常用属性

    DataGrid中的常用属性 $('#dg').datagrid({ url:'datagrid_data.json', columns:[[ {field:'code',title:'Code',w ...

  6. Box Model,边距折叠,内联和块标签,CSSReset

    一.盒子模型(Box Model) 1.1.宽度测试 1.2.溢出测试 1.3.box-sizing属性 1.4.利用CSS画图 二.边距折叠 2.1.概要 2.2.垂直方向外边距合并计算 三.内联与 ...

  7. POJ2513-Colored Sticks

    /*思路:类似图论中“一笔画”问题,两根木棒的相连接的端点是一样的颜色,(a,b)--(b,c)--(c, d)....方法:trie树+并查集, 利用trie树建立字符串和某一个节点的映射,并将这些 ...

  8. IOS学习之初识KVO

    什么是KVO? KVO(Key-Value Observing)键值观察,是一种通过对对象的某一个属性添加观察者,一旦这个属性值发生变化,就会通知当前观察者的一种机制. 该如何使用? 1.注册,指定被 ...

  9. The network bridge on device VMnet0 is not running

    The network bridge on device VMnet0 is not running. The virtual machine will not be able to communic ...

  10. 窥探Swift之协议(Protocol)和委托代理(Delegate)回调的使用

    协议与委托代理回调在之前的博客中也是经常提到和用到的在<Objective-C中的委托(代理)模式>和<iOS开发之窥探UICollectionViewController(四) - ...