nopCommerce程序中以泛型形式对注销器进行了定义,以实现在实体实例发生改变,以泛型方法来删除Redis中的缓存的1个指定实体的所有实例及其键之后, 重新把1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
1 重构Program.cs
//把继承于“IConsumer<>”泛型接口的所有具体实现类的实例,依赖注入到.Net(Core)6框架内置容器中。
//获取所有继承于“IConsumer<>”泛型接口的所有具体实现类的类型实例。
var _consumerList = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes().Where(t => typeof(IConsumer<>).DoesTypeImplementOpenGeneric(t) && !t.IsAbstract))
.ToList();
2 触发器泛型扩展(EventPublisherExtensions)实现
2.1 EntityInsertedEvent
///
///
/// 【触发实体插入--类】
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“插入”操作完成后,通过触发器(EventPublisher)实例,以该类的实例作为参数实例,来调用泛型注销器具体实现类中的方法(HandleEventAsync),来删除Redis中的缓存的指定实体的所有实例及其键之后,
/// 重新把数据库中指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
///
public class EntityInsertedEvent
{
#region 拷贝构造方法
/// 1个指定实体的1个指定实例。
///
/// 【拷贝构造方法】
///
///
/// 摘要:
/// 通过该构造方法中的参数实例,来实例化该类中的同类型属性成员。
///
public EntityInsertedEvent(T entity)
{
Entity = entity;
}
#endregion
#region 属性
///
/// 【单个实体】
///
/// 摘要:
/// 获取/设置1个指定实体的1个指定实例。
/// 注意:
/// 把该属性成员命名为:Instance/EntityInstance更为合适。
///
///
public T Entity { get; }
#endregion
}
2.2 EntityUpdatedEvent
///
///
/// 【触发实体更新--类】
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“更新”操作完成后,通过触发器(EventPublisher)实例,以该类的实例作为参数实例,来调用泛型注销器具体实现类中的方法(HandleEventAsync),来删除Redis中的缓存的指定实体的所有实例及其键之后,
/// 重新把数据库中指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
///
public class EntityUpdatedEvent
{
#region 拷贝构造方法
/// 1个指定实体的1个指定实例。
///
/// 【拷贝构造方法】
///
///
/// 摘要:
/// 通过该构造方法中的参数实例,来实例化该类中的同类型属性成员。
///
public EntityUpdatedEvent(T entity)
{
Entity = entity;
}
#endregion
#region 属性
///
/// 【单个实体】
///
/// 摘要:
/// 获取/设置1个指定实体的1个指定实例。
/// 注意:
/// 把该属性成员命名为:Instance/EntityInstance更为合适。
///
///
public T Entity { get; }
#endregion
}
2.3 EntityDeletedEvent
///
///
/// 【触发实体删除--类】
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“删除”操作完成后,通过触发器(EventPublisher)实例,以该类的实例作为参数实例,来调用泛型注销器具体实现类中的方法(HandleEventAsync),来删除Redis中的缓存的指定实体的所有实例及其键之后,
/// 重新把数据库中指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
///
public class EntityDeletedEvent
{
#region 拷贝构造方法
/// 1个指定实体的1个指定实例。
///
/// 【拷贝构造方法】
///
///
/// 摘要:
/// 通过该构造方法中的参数实例,来实例化该类中的同类型属性成员。
///
public EntityDeletedEvent(T entity)
{
Entity = entity;
}
#endregion
#region 属性
///
/// 【单个实体】
///
/// 摘要:
/// 获取/设置1个指定实体的1个指定实例。
/// 注意:
/// 把该属性成员命名为:Instance/EntityInstance更为合适。
///
///
public T Entity { get; }
#endregion
}
2.4 EventPublisherExtensions
namespace Linkage.Events
{
///
/// 【触发器扩展】
///
///
/// 摘要:
/// 该类中的方法通过泛型操作,在1个指定实体的1个指定实例的对数据库中指定表的“插入”/“更新”/“删除”操作完成之后,获取该指定实体实例;将当前类中的指定方法通过触发器(EventPublisher)实例及其指定实体实例参数,
/// 来删除Redis中的缓存的1个指定实体的所有实例及其键之后, 重新把1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
///
public static class EventPublisherExtensions
{
#region 方法
///
/// 触发器(EventPublisher)实例。
/// 1个指定实体的1个指定实例。
///
/// 【异步触发实体插入】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“插入”操作完成后,操作完成之后,获取该指定实体实例;当前方法通过触发器(EventPublisher)实例及其指定实体实例参数,来删除Redis中的缓存的1个指定实体的所有实例及其键之后,
/// 重新把1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
///
///
public static async Task EntityInsertedAsync
{
await eventPublisher.PublishAsync(new EntityInsertedEvent
}
///
/// 触发器(EventPublisher)实例。
/// 1个指定实体的1个指定实例。
///
/// 【异步触发实体更新】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“更新”操作完成后,获取该指定实体实例;当前方法通过触发器(EventPublisher)实例及其指定实体实例参数,来删除Redis中的缓存的1个指定实体的所有实例及其键之后,
/// 重新把1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
///
///
public static async Task EntityUpdatedAsync
{
await eventPublisher.PublishAsync(new EntityUpdatedEvent
}
///
/// 触发器(EventPublisher)实例。
/// 1个指定实体的1个指定实例。
///
/// 【异步触发实体删除】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“删除”操作完成后,获取该指定实体实例;当前方法通过触发器(EventPublisher)实例及其指定实体实例参数,来删除Redis中的缓存的1个指定实体的所有实例及其键之后,
/// 重新把1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
///
///
public static async Task EntityDeletedAsync
{
await eventPublisher.PublishAsync(new EntityDeletedEvent
}
#endregion
}
}
3 注销器的泛型实现
3.1 泛型抽象注销器类: CacheEventConsumer
注意:
由于.Net(Core)6框架,不能以反射方式实例化泛型类:“CacheEventConsumer”,并依赖注入到.Net(Core)6框架的内置容器中,所以把“CacheEventConsumer”类定义为了“抽象”类,以供能够以反射方式实例化类进行继承。
using Linkage.Events;
using Linkage.Extensions;
using Microsoft.Extensions.Caching.Distributed;
namespace Linkage.Caching
{
///
///
/// 【泛型抽象注销器--类】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“插入”/“更新”/“删除”操作完成后,通过触发器实例,调用当前抽象类中的指定方法,来删除Redis中的1个指定实体的所有实例及其键之后,
/// 重新把数据库中1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
/// 注意:
/// 由于.Net(Core)6框架,不能以反射方式实例化泛型类:“CacheEventConsumer”,并依赖注入到.Net(Core)6框架的内置容器中,所以把“CacheEventConsumer”类定义为了“抽象”类,以供能够以反射方式实例化类进行继承。
///
///
public abstract partial class CacheEventConsumer
IConsumer
IConsumer
IConsumer
{
#region 变量--拷贝构造方法
private readonly IDistributedCache _distributedCache;
public CacheEventConsumer()
{
_distributedCache = ServiceProviderExtension.ServiceProvider.GetService
}
#endregion
#region 方法--私有/保护
/// 1指定实体的1个实例。
///
/// 【异步清理缓存】
///
/// 摘要:
/// 当1个指定实体的1个指定实例,通过异步方法把Redis中的缓存数据被删除成功完成后,返回该异步方法的“完成”执行状态。
///
///
protected virtual Task ClearCacheAsync(TEntity entity)
{
return Task.CompletedTask;
}
/// 1个指定实体的1个指定实例。
/// 实体触发枚举的1个指定实例。
///
/// 【异步触发句柄】
///
/// 摘要:
/// 在执行省/直辖市的插入操作之后,通过触发器实例,调用当前方法,来删除Redis中的缓存的省/直辖市的所有实例及其键之后,
/// 重新把数据库中省/直辖市所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
/// 注意:
/// 为了更加直观,把该方法定义为了特定实现。
///
///
protected virtual async Task ClearCacheAsync(TEntity entity, EntityEventType entityEventType)
{
await _distributedCache.RemoveAsync("StateAll");
await ClearCacheAsync(entity);
}
#endregion
#region 方法
/// 触发实体更新(EntityInsertedEvent)类的1个指定实例。
///
/// 【异步触发句柄】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“插入”操作完成后,通过触发器实例,调用当前方法,来删除Redis中的1个指定实体的所有实例及其键之后,
/// 重新把数据库中1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
///
///
public virtual async Task HandleEventAsync(EntityInsertedEvent
{
await ClearCacheAsync(eventMessage.Entity, EntityEventType.Insert);
}
/// 触发实体更新(EntityUpdatedEvent)类的1个指定实例。
///
/// 【异步触发句柄】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“更新”操作完成后,通过触发器实例,调用当前方法,来删除Redis中的1个指定实体的所有实例及其键之后,
/// 重新把数据库中1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
///
///
public virtual async Task HandleEventAsync(EntityUpdatedEvent
{
await ClearCacheAsync(eventMessage.Entity, EntityEventType.Update);
}
/// 触发实体删除(EntityDeletedEvent)类的1个指定实例。
///
/// 【异步触发句柄】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“删除”操作完成后,通过触发器实例,调用当前方法,来删除Redis中的1个指定实体的所有实例及其键之后,
/// 重新把数据库中1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
///
///
public virtual async Task HandleEventAsync(EntityDeletedEvent
{
await ClearCacheAsync(eventMessage.Entity, EntityEventType.Delete);
}
#endregion
#region 嵌套枚举
///
/// 【实体触发--枚举】
///
/// 摘要:
/// 通过该枚举中的1个成员实例,获取泛型“注销器”中的1个指定(“插入”/“更新”/“删除”)方法,来移除缓存中指定实体的所有数据。
///
///
protected enum EntityEventType
{
///
/// 【插入】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“插入”操作完成后,由“触发器”调用当前的泛型“注销器”中的指定(“插入”)方法,来移除缓存中指定实体的所有实例。
///
///
Insert,
///
/// 【更新】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“更新”操作完成后,由“触发器”调用当前的泛型“注销器”中的指定(“更新”)方法,来移除缓存中指定实体的所有实例。
///
///
Update,
///
/// 【删除】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“删除”操作完成后,由“触发器”调用当前的泛型“注销器”中的指定(“删除”)方法,来移除缓存中指定实体的所有实例。
///
///
Delete
}
#endregion
}
}
3.2泛型注销器的具体实现类: StateOpenTypeConsumer
注意:
由于泛型类:“CacheEventConsumer”,不能以反射方式实例化,并依赖注入到.Net(Core)6框架的内置容器中,所以通过“StateOpenTypeConsumer”类对“CacheEventConsumer”类的继承,把“StateOpenTypeConsumer”类以反射方式实例化后,依赖注入到.Net(Core)6框架的内置容器中。从而达到通过“StateOpenTypeConsumer”类的实例,调用“CacheEventConsumer”类中的指定方法,来删除Redis中的缓存的1个指定实体的所有实例及其键之后,重新把1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
using Linkage.Domain.Directory;
namespace Linkage.Caching
{
///
/// 【省/直辖市抽象注销器--类】
///
/// 摘要:
/// 在1个指定实体的1个指定实例的对数据库中指定表的“插入”/“更新”/“删除”操作完成后,通过触发器实例,调用基类类中的指定方法,来删除Redis中的1个指定实体的所有实例及其键之后,
/// 重新把数据库中1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
/// 注意:
/// 1、由于泛型类:“CacheEventConsumer”,不能以反射方式实例化,并依赖注入到.Net(Core)6框架的内置容器中,所以通过“StateOpenTypeConsumer”类对“CacheEventConsumer”类的继承,
/// 把“StateOpenTypeConsumer”类以反射方式实例化后,依赖注入到.Net(Core)6框架的内置容器中。从而达到通过“StateOpenTypeConsumer”类的实例,调用“CacheEventConsumer”类中的指定方法,
/// 来删除Redis中的缓存的1个指定实体的所有实例及其键之后,重新把1个指定实体所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
/// 2、当然当前类也可以通过对所继承抽象类“CacheEventConsumer”中方法(包含私有方法)的覆写,来实现对Redis中的1个指定实体的所有实例及其键的删除操作,当前为了简化实现,只用于当前类的实例,
/// 其具体的缓存数据的删除操作是通过当前类的实例,调用所继承抽象类“CacheEventConsumer”中的指定方法实现的。
///
///
public partial class StateOpenTypeConsumer : CacheEventConsumer
{
}
}
4重构 CachingConsumerController和CachingConsumerIndex.cshtml
4.1 重构CachingConsumerController
[HttpPost]
public async Task
{
State _state = new State();
_state.Name = stateModel.Name;
await _context.StateDbSet.AddAsync(_state);
await _context.SaveChangesAsync();
//以泛型操作,通过触发器实例,来删除Redis中的缓存的省/直辖市的所有实例及其键之后,
//重新把数据库中省/直辖市所有数据更新加载到Redis的缓存中,从而达到立即把最新的数据在页面上进行渲染显示的目的。
await _eventPublisher.EntityInsertedAsync(_state);
return Json(true);
}
4.2 重构CachingConsumerIndex.cshtml
页面在【添加后立即】渲染显示添加数据(泛型实现)
// 页面在【添加后立即】渲染显示添加数据(泛型实现)
$("#OptenTypeRender").on("click", function () {
if ($("#Name").val() == "" || $("#Name").val() == null) {
alert("省/直辖市不能为空!");
return;
}
$.ajax({
url: "/CachingConsumer/OptenTypeRenderjax",
type: "POST",
datatype: "JSON",
cache: false, //禁用缓存。
data: $("#AddState").serialize(),
success: function (data) {
window.location.href = "/CachingConsumer/Index";
}
});
});
对以上功能更为具体实现和注释见:221029_17Linkage(注销器的泛型实现)。