第9章 CURD操作与MemoryCache缓存的强制清理的实现

news/2024/10/18 1:38:48/

1 重构 Data.Repository<TEntity>

using Core.Caching;

using Core.Domain;

using Core.Events;

using Microsoft.EntityFrameworkCore;

namespace Data

{

    ///<typeparam name="TEntity">泛型类型实例(这里特指:1个指定实体的类型实例)</typeparam>

    /// <summary>

    /// 【仓储--类】

    /// <remarks>

    /// 摘要:

    ///     通过该类中的泛型方法成员实例,为指定实体的CURD操作提供方法支撑。

    /// </remarks>

    /// </summary>

    public class Repository<TEntity> : IRepository<TEntity> where TEntity : BaseEntity

    {

        #region 变量--私有/保护

        private readonly IEventPublisher _eventPublisher;

        private readonly EFCoreContext _context;

        private readonly IStaticCacheManager _staticCacheManager;

        #endregion

        #region 拷贝构造方法

        /// <param name="context">EFCore上下文类的1个指定实例 </param>

        /// <param name="staticCacheManager">分布式缓存接口实例。</param>

        /// <summary>

        /// 【拷贝构造方法】

        /// <remarks>

        /// 摘要:

        ///     通过拷贝构造方法,对当前类中的同名变量成员进行实例化。

        /// </remarks>

        /// </summary>

        public Repository(IEventPublisher eventPublisher,

            EFCoreContext context,

            IStaticCacheManager staticCacheManager)

        {

            _eventPublisher = eventPublisher;

            _context = context;

            _staticCacheManager = staticCacheManager;

        }

        #endregion

        #region 属性--接口实现

        /// <summary>

        /// 【表】

        /// <remarks>

        /// 摘要:

        ///     获取/设置1个指定实体的可查询接口(IQueryable)1个指定实例,该实例中存储着1个指定实体的所有实例。

        /// 说明:

        ///     1IQueryable继承于IEnumerable。在此之上,增加了一些属性和扩展方法。但是,关键不在这些属性和方法上,而是Queryable实现了革命性的新行为特性。

        ///     2IQueryable革命性的特性是且有"延迟加载(lazy loading)特性,可查询接口(IQueryable)1个指定实例在内存中没有任何的实例数据,只调用该实例的ToList/ToListAsync方法才能把实例数据加载的内存中。

        /// </remarks>

        /// </summary>

        public IQueryable<TEntity> Table => _context.GetDbSet<TEntity>().AsQueryable();

        #endregion

        #region 方法--私有/保护

        /// <param name="getAllAsync">1个具有返回值的泛型异步委托方法实例,该泛型异步委托方法实例用于获取1个指定实体的所有实例。</param>

        /// <param name="getCacheKey">1个具有返回值的委托方法实例,委托方法实例用于获取缓存键类的1个指定实例。</param>

        /// <summary>

        /// 【异步获取实体】

        /// <remarks>

        /// 摘要:

        ///     直接从指定表中1个指定实体的所有实例;或从分布式缓存数据库获取1个指定实体的所有实例,并把所有实例存储到列表实例中。

        /// </remarks>

        /// <returns>

        ///    列表实例,该实例存储着1个指定实体的所有实例。

        /// </returns>

        /// </summary>

        protected virtual async Task<IList<TEntity>> GetEntitiesAsync(Func<Task<IList<TEntity>>> getAllAsync, Func<IStaticCacheManager, CacheKey> getCacheKey)

        {

            //如果不存在缓存键实例,则直接从持久化表中获取1个指定实体的所有实例。

            if (getCacheKey == null)

                return await getAllAsync();

            //如果存在缓存键实例,则从分布式缓存数据库获取1个指定实体的所有实例。

            var cacheKey = getCacheKey(_staticCacheManager)

                           ?? _staticCacheManager.PrepareKeyForDefaultCache(EntityCacheDefaults<TEntity>.AllCacheKey);

            return await _staticCacheManager.GetAsync(cacheKey, getAllAsync);

        }

        /// <param name="query">“IQueryable”实例进行存储的1个指定类型的数据源(枚举数接口实例=长串型,包含:数组、列表、字典等)</param>

        /// <param name="includeDeleted">指示是否包含指定实体中所有的逻辑删除项。</param>

        /// <summary>

        /// 【逻辑删除过滤】

        /// <remarks>

        /// 摘要:

        ///     获取指定实体中所有的实例或未被逻辑删除的所有实例。

        /// 说明:

        ///     1、由于政策因素,一些数据不能进行物理删除,只能进行逻辑删除,但以要在数据加载时,只加载非逻辑删除实例,达到减少内存消耗目标。

        ///     2、逻辑删除实例只具有数据纪录作用,而在渲染显示时已经无任何的意义,所以必须对处于逻辑删除状态的实例进行过滤。

        ///     3、在实际网站中,所有的数据都财富和资本,所以程序中只调用逻辑联删除而不调用物理删除。

        /// </remarks>

        /// <returns>

        ///    1个指定实体的可查询接口(IQueryable)实例,该实例中存储着1个指定实体的所有的实例或未被逻辑删除的所有实例。

        /// </returns>

        /// </summary>

        protected virtual IQueryable<TEntity> AddDeletedFilter(IQueryable<TEntity> query, in bool includeDeleted)

        {

            if (includeDeleted)

                return query;

            if (typeof(TEntity).GetInterface(nameof(ISoftDeletedEntity)) == null)

                return query;

            return query.OfType<ISoftDeletedEntity>().Where(entry => !entry.Deleted).OfType<TEntity>();

        }

        #endregion

        #region 方法--接口实现

        /// <param name="func">1个具有返回值的泛型委托方法实例,该泛型委托方法实例用于获取1个指定实体的1/n个实例。</param>

        /// <param name="getCacheKey">1个具有返回值的委托方法实例,委托方法实例用于获取缓存键类的1个指定实例。</param>

        /// <param name="includeDeleted">指示是否包含指定实体中所有的逻辑删除项,默认值:true,即指定实体的所有实例。</param>

        /// <summary>

        /// 【异步获取所有实例】

        /// <remarks>

        /// 摘要:

        ///     以异步操作方式,直接从数据库的指定表中或缓存数据库中获取1个指定实体的所有实例,并把这些实例存储到1个列表实例中。

        /// </remarks>

        /// <returns>

        ///    1个列表实例,该实例存储着1个指定实体的所有实例。

        /// </returns>

        /// </summary>

        public virtual async Task<IList<TEntity>> GetAllAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> func = null, Func<IStaticCacheManager, CacheKey> getCacheKey = null, bool includeDeleted = true)

        {

            async Task<IList<TEntity>> getAllAsync()

            {

                var query = AddDeletedFilter(Table, includeDeleted);

                query = func != null ? func(query) : query;

                return await query.ToListAsync();

            }

            return await GetEntitiesAsync(getAllAsync, getCacheKey);

        }

        /// <param name="id">1个指定的长整型编号值。</param>

        /// <param name="getCacheKey">1个具有返回值的委托方法实例,委托方法实例用于获取缓存键类的1个指定实例,默认值:null,即不存在缓存键实例。</param>

        /// <param name="includeDeleted">指示是否包含指定实体中所有的逻辑删除项,默认值:true,即指定实体的所有实例。</param>

        /// <summary>

        /// 【异步通过编号值获取】

        /// <remarks>

        /// 摘要:

        ///     直接从指定表中1个指定实体的1个指定实例;或从分布式缓存数据库获取1个指定实体的1个指定实例(即使该实例处于逻辑删除状态,也获取该实例)

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个指定实体的1个指定实例。

        /// </returns>

        /// </summary>

        public virtual async Task<TEntity> GetByIdAsync(long? id, Func<IStaticCacheManager, CacheKey> getCacheKey = null, bool includeDeleted = true)

        {

            //如果1个指定的长整型编号值,没有值;或该编号值为:0,则直接退出当前方法。

            if (!id.HasValue || id == 0)

                return null;

            // 通过异步委托方法,从指定表中获取1个指定实体的1个指定实例。

            async Task<TEntity> getEntityAsync()

            {

                return await AddDeletedFilter(Table, includeDeleted).FirstOrDefaultAsync(entity => entity.Id == Convert.ToInt32(id));

            }

            //如果不存在缓存键实例,则直接返回1个指定实体的1个指定实例。

            if (getCacheKey == null)

                return await getEntityAsync();

            //如果存在缓存键实例,则把1个指定实体的1个指定实例,缓存到分布式缓存数据库中。

            var cacheKey = getCacheKey(_staticCacheManager)

                ?? _staticCacheManager.PrepareKeyForDefaultCache(EntityCacheDefaults<TEntity>.ByIdCacheKey, id);

            //从分布式缓存数据库返回1个指定实体的1个指定实例。

            return await _staticCacheManager.GetAsync(cacheKey, getEntityAsync);

        }

        /// <param name="entity">列表实例,该实例存储着指定实体的1/n个指定实例。</param>

        /// <param name="publishEvent">指示在指定实体的1个指定实例持久化更新操作成功后,是否从缓存数据库中强制移除与该实体相关的缓存项,默认值:true,即从缓存数据库中强制移除与该实体相关的缓存项。</param>

        /// <summary>

        /// 【异步插入】

        /// <remarks>

        /// 摘要:

        ///     把指定实体的1/n个指定实例持久化插入到指定表中后,并从缓存数据库中移除与该实体相关的所有缓存项。

        /// </remarks>

        /// </summary>

        public virtual async Task InsertAsync(TEntity entity, bool publishEvent = true)

        {

            if (entity == null)

                throw new ArgumentNullException(nameof(entity));

            await _context.GetDbSet<TEntity>().AddAsync(entity);

            await _context.SaveChangesAsync();

            //在插入操作执行后,从分布式缓存数据为中强制移除指定体相关的所有缓存项,从而缓存数据库重新缓存加载指定实体的所有实例,最终避免该实体的列表出现渲染显示异常。

            if (publishEvent)

            await _eventPublisher.EntityInsertedAsync(entity);

        }

        /// <param name=" entities">列表实例,该实例存储着指定实体的1/n个指定实例。</param>

        /// <param name="publishEvent">指示在指定实体的1个指定实例持久化更新操作成功后,是否从缓存数据库中移除与该实体相关的缓存项,默认值:true,即从缓存数据库中移除与该实体相关的缓存项。</param>

        /// <summary>

        /// 【异步插入】

        /// <remarks>

        /// 摘要:

        ///     把指定实体的1/n个指定实例持久化插入到指定表中后,并从缓存数据库中移除与该实体相关的所有缓存项。

        /// </remarks>

        /// </summary>

        public virtual async Task InsertAsync(IList<TEntity> entities, bool publishEvent = true)

        {

            if (entities == null)

                throw new ArgumentNullException(nameof(entities));

            _context.GetDbSet<TEntity>().AddRange(entities);

            await _context.SaveChangesAsync();

            //如果不存在触发器实例,则直接退出当前方法。

            if (!publishEvent)

                return;

            //如果存在触发器实例,从分布式缓存数据为中强制移除指定体相关的所有缓存项。

            foreach (var entity in entities)

                await _eventPublisher.EntityInsertedAsync(entity);

        }

        /// <param name="entity">指定实体的1个指定实例。</param>

        /// <param name="publishEvent">指示在指定实体的1个指定实例持久化更新操作成功后,是否从缓存数据库中移除与该实体相关的缓存项,默认值:true,即从缓存数据库中移除与该实体相关的缓存项。</param>

        /// <summary>

        /// 【异步更新】

        /// <remarks>

        /// 摘要:

        ///     把指定实体的1个指定实例持久化更新到指定表中后,并从缓存数据库中移除与该实体相关的所有缓存项。

        /// </remarks>

        /// </summary>

        public virtual async Task UpdateAsync(TEntity entity, bool publishEvent = true)

        {

            if (entity == null)

                throw new ArgumentNullException(nameof(entity));

            _context.GetDbSet<TEntity>().Update(entity);

            await _context.SaveChangesAsync();

            //在更新操作执行后,从分布式缓存数据为中强制移除指定体相关的所有缓存项。

            if (publishEvent)

                await _eventPublisher.EntityUpdatedAsync(entity);

        }

        /// <param name=" entities">列表实例,该实例存储着指定实体的1/n个指定实例。</param>

        /// <param name="publishEvent">指示在指定实体的1个指定实例持久化更新操作成功后,是否从缓存数据库中移除与该实体相关的缓存项,默认值:true,即从缓存数据库中移除与该实体相关的缓存项。</param>

        /// <summary>

        /// 【异步更新】

        /// <remarks>

        /// 摘要:

        ///     把指定实体的1/n个指定实例持久化更新到指定表中后,并从缓存数据库中移除与该实体相关的所有缓存项。

        /// </remarks>

        /// </summary>

        public virtual async Task UpdateAsync(IList<TEntity> entities, bool publishEvent = true)

        {

            if (entities == null)

                throw new ArgumentNullException(nameof(entities));

            if (entities.Count == 0)

                return;

            _context.GetDbSet<TEntity>().UpdateRange(entities);

            await _context.SaveChangesAsync();

            //如果不存在触发器实例,则直接退出当前方法。

            if (!publishEvent)

                return;

            //如果存在触发器实例,从分布式缓存数据为中强制移除指定体相关的所有缓存项。

            foreach (var entity in entities)

                await _eventPublisher.EntityUpdatedAsync(entity);

        }

        /// <param name="entity">指定实体的1个指定实例。</param>

        /// <param name="publishEvent">指示在指定实体的1个指定实例持久化更新操作成功后,是否从缓存数据库中移除与该实体相关的缓存项,默认值:true,即从缓存数据库中移除与该实体相关的缓存项。</param>

        /// <summary>

        /// 【异步删除】

        /// <remarks>

        /// 摘要:

        ///     把指定实体的1个指定实例从指定表中物理删除后,并从缓存数据库中移除与该实体相关的所有缓存项。

        /// </remarks>

        /// </summary>

        public virtual async Task DeleteAsync(TEntity entity, bool publishEvent = true)

        {

            switch (entity)

            {

                case null:

                    throw new ArgumentNullException(nameof(entity));

                case ISoftDeletedEntity softDeletedEntity:

                    softDeletedEntity.Deleted = true;

                    _context.GetDbSet<TEntity>().Update(entity);

                    await _context.SaveChangesAsync();

                    break;

                default:

                    _context.GetDbSet<TEntity>().Remove(entity);

                    await _context.SaveChangesAsync();

                    break;

            }

            //如果存在触发器实例,从分布式缓存数据为中强制移除指定体相关的所有缓存项。

            if (publishEvent)

                await _eventPublisher.EntityDeletedAsync(entity);

        }

        /// <param name="entities">列表实例,该实例存储着指定实体的1/n个指定实例。</param>

        /// <param name="publishEvent">指示在指定实体的1个指定实例持久化更新操作成功后,是否从缓存数据库中移除与该实体相关的缓存项,默认值:true,即从缓存数据库中移除与该实体相关的缓存项。</param>

        /// <summary>

        /// 【异步删除】

        /// <remarks>

        /// 摘要:

        ///     把指定实体的1/n个指定实例从指定表中物理删除后,并从缓存数据库中移除与该实体相关的所有缓存项。

        /// </remarks>

        /// </summary>

        public virtual async Task DeleteAsync(IList<TEntity> entities, bool publishEvent = true)

        {

            if (entities == null)

                throw new ArgumentNullException(nameof(entities));

            if (entities.OfType<ISoftDeletedEntity>().Any())

            {

                foreach (var entity in entities)

                {

                    if (entity is ISoftDeletedEntity softDeletedEntity)

                    {

                        softDeletedEntity.Deleted = true;

                        _context.GetDbSet<TEntity>().Update(entity);

                        await _context.SaveChangesAsync();

                    }

                }

            }

            else

            {

                _context.GetDbSet<TEntity>().RemoveRange(entities);

                await _context.SaveChangesAsync();

            }

            //如果不存在触发器实例,则直接退出当前方法。

            if (!publishEvent)

                return;

            //如果存在触发器实例,从分布式缓存数据为中强制移除指定体相关的所有缓存项。

            foreach (var entity in entities)

                await _eventPublisher.EntityDeletedAsync(entity);

        }

        #endregion

    }

}

2 Web.Controllers.CacheController

using Core.Domain.Customers;

using Data;

using Microsoft.AspNetCore.Mvc;

namespace Web.Controllers

{

    public class CacheController : Controller

    {

        private readonly IRepository<Role> _repositoryRole;

        public CacheController(IRepository<Role> repositoryRole)

        {

            _repositoryRole = repositoryRole;

        }

        public async Task<IActionResult> Index()

        {

            //IList<Role> _roleList = await _repositoryRole.GetAllAsync(query => { return query; }, null);

            //如果cache => default不为:null,那么在添加实例时必须触发指定实例所有相关缓存的清理操作,列表实例会由继续调用中以前所缓存的数据(不包含新添加的实例),从而导致新添加的实例不能在列表视图中渲染显示出来。

            //如果cache => default为:null,则直接从数据库指定表中获取数据,所以不存在上述问题。

            IList<Role> _roleList = await _repositoryRole.GetAllAsync(query => { return query; }, cache => default);

            return View(_roleList);

        }

        public IActionResult Create()

        {

            return View();

        }

        [HttpPost]

        public async Task<IActionResult> Create(Role model)

        {

            model.Active = true;

            await _repositoryRole.InsertAsync(model);

            return RedirectToAction("Index");

        }

        public async Task<IActionResult> Edit(long id)

        {

            Role _role = await _repositoryRole.GetByIdAsync(id, cache => default);

            return View(_role);

        }

        [HttpPost]

        public async Task<IActionResult> Edit(Role model)

        {

            await _repositoryRole.UpdateAsync(model);

            return RedirectToAction("Index");

        }

        public async Task<IActionResult> Delete(long id)

        {

            //由于“_role”实例用于物理删除操作,所以“GetByIdAsync”方法不用包含“cache => default”参数实例。

            Role _role = await _repositoryRole.GetByIdAsync(id);

            await _repositoryRole.DeleteAsync(_role);

            return RedirectToAction("Index");

        }

        public async Task<IActionResult> CreateTen()

        {

            List<Role> _roleList =new List<Role>();

            for(int i = 0; i < 10; i++)

            {

               Role _role = new Role() { Name="RoleTen", Active=true, Remark=$"Remark_{i}"};

                _roleList.Add(_role);

            }

            await _repositoryRole.InsertAsync(_roleList);

            return RedirectToAction("Index");

        }

        public async Task<IActionResult> EditTen()

        {

            //由于是批量编辑操作,所以“GetAllAsync”方法不用包含“cache => default”参数实例。

            IList<Role> _roleList = await _repositoryRole.GetAllAsync(query => { return query.Where(role=>role.Name.Equals("RoleTen"));});

            for (int i = 0; i < _roleList.Count; i++)

            {

                _roleList[i].Remark += "_Edit";

            }

            await _repositoryRole.UpdateAsync(_roleList);

            return RedirectToAction("Index");

        }

        public async Task<IActionResult> DeleteTen()

        {

            //由于是批量删除操作,所以“GetAllAsync”方法不用包含“cache => default”参数实例。

            IList<Role> _roleList = await _repositoryRole.GetAllAsync(query => { return query.Where(role => role.Name.Equals("RoleTen")); });

            await _repositoryRole.DeleteAsync(_roleList);

            return RedirectToAction("Index");

        }

    }

}

对以上功能更为具体实现和注释见230510_009ShopRazor(CURD操作与MemoryCache缓存的强制清理的实现)。


http://www.ppmy.cn/news/64616.html

相关文章

删数的问题

删数问题 题目描述 键盘输入一个高精度的正整数 N N N&#xff08;不超过 250 250 250 位&#xff09;&#xff0c;去掉其中任意 k k k 个数字后剩下的数字按原左右次序将组成一个新的非负整数。编程对给定的 N N N 和 k k k&#xff0c;寻找一种方案使得剩下的数字组成…

curl命令漫谈

curl命令简单解释。 文章目录 一. 什么是curl二. 基本用法三. 简单使用 一. 什么是curl curl 是一种的命令行工具&#xff0c;用来请求 Web 服务器。它的名字实际上就是客户端&#xff08;client&#xff09;的 URL 工具的意思。 通常&#xff0c;我们也会使用该命令进行服务…

【Android 常见问题(四)】-kotlin

目录 说一下kotlin的优缺点。let和with的区别扩展函数kotlin的lateinit和by lazy的区别构造函数有哪几种协程 说一下kotlin的优缺点。let和with的区别 Kotlin 语言的优点&#xff1a; 增强的类型安全&#xff1a;使用 Kotlin 可以减少空指针异常等运行时错误&#xff0c;提高…

安装endnote 之后打开word出现乱码

文章目录 1 安装endnote 之后打开word出现乱码3 EndNote X9 插入参考文献常见问题总结4 EndNote X9 快速上手教程&#xff08;毕业论文参考文献管理器&#xff09; 1 安装endnote 之后打开word出现乱码 安装了endnote&#xff0c;打开文档后&#xff0c;目录显示这样&#xff…

【Fluent】利用TUI命令在保存或导出文件时,给文件名加上时间步长、流动时间、迭代步数等求解过程变量的自动编码

一、问题背景 在CSDN的一篇博客&#xff08;从Fluent导出数据到txt文档&#xff09;中&#xff0c;一位博主分享了一串导出求解数据的TUI命令。 file/export/ascii data%t.txt () yes h2s y-velocity x-velocity q no 当时我不知道里面的%t是啥意思&#xff0c;估计是跟时间…

001+limou+MySQL的基础命令

0.前言 您好&#xff0c;这里是limou3434的一篇个人博文&#xff0c;感兴趣的话您也可以看看我的其他文章。本博文是借鉴于李小威前辈所著的书籍《SQL 基础教程》所成的博文笔记&#xff0c;这本书真的很适合新手学习数据库相关的内容。本次我想给您带来的是关于MySQL的一些基…

和月薪5W的聊过后,才发现自己一直在打杂···

前几天和一个朋友聊面试&#xff0c;他说上个月同时拿到了腾讯和阿里的offer&#xff0c;最后选择了阿里。 我了解了下他的面试过程&#xff0c;就一点&#xff0c;不管是阿里还是腾讯的面试&#xff0c;这个级别的程序员&#xff0c;都会考察项目管理能力&#xff0c;并且权重…

如何在Docker容器中运行GUI图形界面程序并能输入中文

前言 "Docker container is not VMware". 但是我发现相比于虚拟机&#xff0c;容器技术用于开发环境管理也是非常地nice。唯一痛点就是不能使用IDE写代码&#xff0c;因为容器通常是以命令行形式给我们操作的。经过研究&#xff0c;找到了一种方法来解决这个问题&…