上一篇讲解了如何在ABP中使用Redis Cache,虽然能够正常的访问Redis,但是Redis里的信息无法同步更新。本文将讲解如何实现Redis Cache与实体同步更新。要实现数据的同步更新,我们能够想到的最基本、最简单、也是复杂的方法:在每一个增、删、改的方法里添加同步缓存的代码,说它最简单,是因为技术实现非常简单,就是在每一个方法里多加一句代码;说它复杂,是因为这样的写的话,会出现很多重复的代码,并且容易出现遗漏,维护起来很不方便。那么有没有一种更简单的方式呢,在数据出现变化后,触发一个事件,主动通知订阅者执行相关操作呢?答案是肯定的,我们可以通过注册领域事件来实现,在ABP中,实体增加、删除、修改后会触发相关事件,只要注册就可以了。要注册事件,有两种方式可以实现,第一是自动注册,实现IEventHandler就可以了,ABP会自动注册;第二是通过IEventBus的Register方法手动注册。ABP中推荐使用自动注册的方式实现,本文也会采用第一种方式实现。下面我们就来看具体的实现方式,首先增加一个处理缓存同步的接口ICacheSyncService,代码如下(本文的代码是在上一篇的基础之上编写的):
ICacheSyncService.cs
public interface ICacheSyncService { void Add<TEntity>(TEntity entity) where TEntity : class, IEntity<int>; void Remove<TEntity>(int id) where TEntity : class, IEntity<int>; void Update<TEntity>(TEntity entity) where TEntity : class, IEntity<int>; }
CacheSyncService.cs
public class CacheSyncService : ICacheSyncService, ISingletonDependency { public ICacheService CacheService { get; set; } public void Add<TEntity>(TEntity entity) where TEntity : class, IEntity<int> { CacheService.Set(entity.Id, entity); } public void Remove<TEntity>(int id) where TEntity : class, IEntity<int> { CacheService.Remove<int, TEntity>(id); } public void Update<TEntity>(TEntity entity) where TEntity : class, IEntity<int> { CacheService.Set(entity.Id, entity); } }
第二增加一个处理实体事件的泛型基类EntityChangedHandlerBase<TEntity>,代码如下:
public abstract class EntityChangedHandlerBase<TEntity>: ISingletonDependency, IEventHandler<EntityCreatedEventData<TEntity>>, IEventHandler<EntityDeletedEventData<TEntity>>, IEventHandler<EntityUpdatedEventData<TEntity>> where TEntity : class, IEntity<int> { public ICacheSyncService CacheSyncService { get; set; } public virtual void HandleEvent(EntityCreatedEventData<TEntity> eventData) { CacheSyncService.Add(eventData.Entity); } public virtual void HandleEvent(EntityDeletedEventData<TEntity> eventData) { CacheSyncService.Remove<TEntity>(eventData.Entity.Id); } public virtual void HandleEvent(EntityUpdatedEventData<TEntity> eventData) { CacheSyncService.Update(eventData.Entity); } }
第三,增加一个需要进行缓存同步的实体类,继承自EntityChangedHandlerBase<TEntity>就可以了,不需要编写任何代码,如果有特殊情况,可以重新HandleEvent方法,代码如下:
public class ArticleChangedHandler : EntityChangedHandlerBase<Article> { }
至此,缓存同步的全部代码已编写完成,我们运行来看看效果:
我们添加文章后,什么操作也不做,直接到Redis里查看是否有新增的数据,如果有新增的数据表示缓存能够自动更新了(删除和修改是一样的,这里就不编写相关代码了),直接看单元测试代码。
public class CacheSync_Tests : UsingRedisInAbpTestBase { [Fact] public void Test_Entity_Changed_Event() { LoginAsHostAdmin(); var title = "unit_test"; var articleId = 0; var service = Resolve<TestCacheSyncService>(); service.IsCreatedEventFired.ShouldBeFalse(); service.IsUpdatedEventFired.ShouldBeFalse(); service.IsDeletedEventFired.ShouldBeFalse(); //新增文章测试 UsingDbContext(c =>c.Articles.Add(new Article {Title = title})); service.IsCreatedEventFired.ShouldBe(true); //更新文章测试 UsingDbContext(c => { var article=c.Articles.First(); c.Articles.Attach(article); article.Title = "new_title"; }); service.IsUpdatedEventFired.ShouldBe(true); //删除文章测试 UsingDbContext(c => { var article = c.Articles.First(); c.Articles.Remove(article); }); service.IsDeletedEventFired.ShouldBe(true); } }
TestSyncCache.cs代码:
public class TestCacheSyncService : ICacheSyncService { public bool IsCreatedEventFired { get; set; } public bool IsDeletedEventFired { get; set; } public bool IsUpdatedEventFired { get; set; } public void Add<TEntity>(TEntity entity) where TEntity : class, IEntity<int> { IsCreatedEventFired = true; } public void Remove<TEntity>(int id) where TEntity : class, IEntity<int> { IsDeletedEventFired = true; } public void Update<TEntity>(TEntity entity) where TEntity : class, IEntity<int> { IsUpdatedEventFired = true; } }
通过以上方式已实现了ABP中Redis缓存的读取和设置,也实现了缓存的同步更新,如果要在ABP中使用其他缓存也是一样的,只需要把缓存实现部分换成其他缓存就行。本文的所有源代码下载地址:
https://files.cnblogs.com/files/loyldg/UsingRedisInAbp_2.src.rar