在ASP.NET Core WebAPI 中使用轻量级的方式实现一个支持持久化的缓存组件

前言

在 WebAPI 开发中,缓存是一种常用的优化手段。Redis 是广泛使用的缓存解决方案,但在某些场景下,我们可能不希望引入第三方依赖,而是希望使用轻量级的方式实现一个支持持久化的缓存组件,满足以下需求:

  • 缓存持久化:重启后缓存可以恢复。
  • 过期删除:支持设置缓存过期时间。
  • 基本操作:支持常见的增、删、查操作。

本文将指导如何设计和实现一个符合上述需求的缓存组件。

需求分析与设计思路

要实现这样的缓存组件,我们需要解决以下几个关键问题:

  1. 数据存储
    选择适合持久化的数据存储方式。SQLite 是一个很好的选择,因为它内置于 .NET,性能良好,且无需额外安装服务。

  2. 过期管理
    需要定期清理过期缓存,可以通过后台定时任务扫描和清理。

  3. 高效的操作接口
    提供易用的 SetGetRemove 等接口,并封装为服务供 API 使用。

实现步骤

1. 定义缓存模型

定义缓存的基本结构,包含键、值、过期时间等信息。

public class CacheItem
{public string Key { get; set; } = null!;public string Value { get; set; } = null!;public DateTime Expiration { get; set; }
}

2. 创建 SQLite 数据存储

在项目中配置 SQLite 数据库,用于存储缓存数据。

配置 SQLite 数据库
 <Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>net8.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable></PropertyGroup><ItemGroup><PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.11" /><PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" /><PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" /><PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" /><PackageReference Include="SQLitePCLRaw.core" Version="2.1.10" /><PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" Version="2.1.10" /><PackageReference Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.1.10" /></ItemGroup></Project>
初始化数据库

创建一个帮助类用于初始化和操作 SQLite 数据库。

using Microsoft.Data.Sqlite;
using SQLitePCL;namespace SqliteCache
{public class CacheDbContext{private readonly string _connectionString = "Data Source=cache.db";public CacheDbContext(){Batteries.Init();using var connection = new SqliteConnection(_connectionString);connection.Open();var command = connection.CreateCommand();command.CommandText = @"CREATE TABLE IF NOT EXISTS Cache (Key TEXT PRIMARY KEY,Value TEXT NOT NULL,Expiration TEXT NOT NULL);";command.ExecuteNonQuery();}public void AddOrUpdate(CacheItem item){using var connection = new SqliteConnection(_connectionString);connection.Open();var command = connection.CreateCommand();command.CommandText = @"INSERT INTO Cache (Key, Value, Expiration)VALUES (@Key, @Value, @Expiration)ON CONFLICT(Key) DO UPDATE SETValue = excluded.Value,Expiration = excluded.Expiration;";command.Parameters.AddWithValue("@Key", item.Key);command.Parameters.AddWithValue("@Value", item.Value);command.Parameters.AddWithValue("@Expiration", item.Expiration.ToString("o"));command.ExecuteNonQuery();}public CacheItem? Get(string key){using var connection = new SqliteConnection(_connectionString);connection.Open();var command = connection.CreateCommand();command.CommandText = "SELECT Key, Value, Expiration FROM Cache WHERE Key = @Key";command.Parameters.AddWithValue("@Key", key);using var reader = command.ExecuteReader();if (reader.Read()){return new CacheItem{Key = reader.GetString(0),Value = reader.GetString(1),Expiration = DateTime.Parse(reader.GetString(2))};}return null;}public void Remove(string key){using var connection = new SqliteConnection(_connectionString);connection.Open();var command = connection.CreateCommand();command.CommandText = "DELETE FROM Cache WHERE Key = @Key";command.Parameters.AddWithValue("@Key", key);command.ExecuteNonQuery();}public void ClearExpired(){using var connection = new SqliteConnection(_connectionString);connection.Open();var command = connection.CreateCommand();command.CommandText = "DELETE FROM Cache WHERE Expiration < @Now";command.Parameters.AddWithValue("@Now", DateTime.UtcNow.ToString("o"));command.ExecuteNonQuery();}}
}

3. 创建缓存服务

封装数据库操作,提供易用的缓存接口。

namespace SqliteCache
{public class PersistentCacheService{private readonly CacheDbContext _dbContext;public PersistentCacheService(){_dbContext = new CacheDbContext();}public void Set(string key, string value, TimeSpan expiration){var cacheItem = new CacheItem{Key = key,Value = value,Expiration = DateTime.UtcNow.Add(expiration)};_dbContext.AddOrUpdate(cacheItem);}public string? Get(string key){var item = _dbContext.Get(key);if (item == null || item.Expiration <= DateTime.UtcNow){_dbContext.Remove(key); // 自动删除过期项return null;}return item.Value;}public void Remove(string key){_dbContext.Remove(key);}}
}

4. 定期清理过期缓存

利用 ASP.NET Core 的后台任务机制清理过期缓存。

配置后台服务
using Microsoft.Extensions.Hosting;namespace SqliteCache
{public class CacheCleanupService : BackgroundService{private readonly CacheDbContext _dbContext = new();protected override async Task ExecuteAsync(CancellationToken stoppingToken){while (!stoppingToken.IsCancellationRequested){_dbContext.ClearExpired();await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);}}}
}
注册服务

SqliteCacheServiceCollectionExtensions.cs 中注册缓存服务和后台任务。

using SqliteCache;
namespace Microsoft.Extensions.DependencyInjection
{public static class SqliteCacheServiceCollectionExtensions{public static IServiceCollection AddSqliteCache(this IServiceCollection services){services.AddSingleton<PersistentCacheService>();services.AddHostedService<CacheCleanupService>();return services;}}
}

5. 使用缓存服务

新增一个asp.net core webapi项目 CacheProject,添加项目引用SqliteCache。在Main函数中添加服务引用。

namespace CacheProject
{public class Program{public static void Main(string[] args){var builder = WebApplication.CreateBuilder(args);builder.Services.AddControllers();builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();//引入Sqlitecache缓存组件builder.Services.AddSqliteCache();var app = builder.Build();// Configure the HTTP request pipeline.if (app.Environment.IsDevelopment()){app.UseSwagger();app.UseSwaggerUI();}app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();app.Run();}}
}

在控制器中注入并使用缓存服务。

[ApiController]
[Route("api/[controller]")]
public class CacheController : ControllerBase
{private readonly PersistentCacheService _cacheService;public CacheController(PersistentCacheService cacheService){_cacheService = cacheService;}[HttpPost("set")]public IActionResult Set(string key, string value, int expirationMinutes){_cacheService.Set(key, value, TimeSpan.FromMinutes(expirationMinutes));return Ok("Cached successfully.");}[HttpGet("get")]public IActionResult Get(string key){var value = _cacheService.Get(key);if (value == null)return NotFound("Key not found or expired.");return Ok(value);}[HttpDelete("remove")]public IActionResult Remove(string key){_cacheService.Remove(key);return Ok("Removed successfully.");}
}

6.运行

启动WebApi项目

info: Microsoft.Hosting.Lifetime[14]Now listening on: https://localhost:7193
info: Microsoft.Hosting.Lifetime[14]Now listening on: http://localhost:5217
info: Microsoft.Hosting.Lifetime[0]Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]Content root path: E:\F\Projects\CSharp\CacheProject\CacheProject
  1. 浏览器查看
    在这里插入图片描述
  2. 写入缓存
    在这里插入图片描述
  3. 读缓存和重启程序
    在这里插入图片描述
  4. 缓存已过期
    在这里插入图片描述

总结

本文展示了如何在 ASP.NET Core WebAPI 中,使用 SQLite 构建一个支持持久化和过期管理的缓存组件。通过以上步骤,我们实现了一个轻量级、易扩展的缓存系统,无需引入 Redis 等第三方工具,同时满足了性能和持久化的需求。

扩展思路

  • 使用 JSON 或 Protobuf 对值进行序列化,支持更复杂的数据类型。
  • 提供缓存命中率统计等高级功能。
  • 增加分布式缓存支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/478271.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【区块链】深入理解椭圆曲线密码学(ECC)

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 深入理解椭圆曲线密码学(ECC)1. 概述2. 椭圆曲线的数学基础2.1 基本定义2.2 有限…

内存不足引发C++程序闪退崩溃问题的分析与总结

目录 1、内存不足一般出现在32位程序中 2、内存不足时会导致malloc或new申请内存失败 2.1、malloc申请内存失败&#xff0c;返回NULL 2.2、new申请内存失败&#xff0c;抛出异常 3、内存不足项目实战案例中相关细节与要点说明 3.1、内存不足导致malloc申请内存失败&#…

设计模式之代理模式(模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景)

前言&#xff1a; 写写CRUD&#xff0c;不会的百度一下&#xff0c;就完事了&#xff0c;总觉得别人问的东西像在造火箭一样。但在高体量、高并发的业务场景下&#xff0c;每一次的压测优化&#xff0c;性能提升&#xff0c;都像在研究一道数学题一样&#xff0c;反复的锤炼&am…

Java项目实战II基于微信小程序的图书馆自习室座位预约平台(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 在知识爆炸的时代&#xff0c;图书馆和…

【机器学习】——卷积与循环的交响曲:神经网络模型在现代科技中的协奏

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

智慧社区管理系统平台提升物业运营效率与用户体验

内容概要 智慧社区管理系统平台是一个集成了多项功能的综合性解决方案&#xff0c;旨在通过先进的技术手段提升物业管理的效率和居民的生活质量。该平台不仅关注物业运营的各个方面&#xff0c;还强调用户体验的重要性。随着科技的发展&#xff0c;社区管理方式正发生着翻天覆…

使用脚本实现hadoop-yarn-flink自动化部署

本文使用脚本实现hadoop-yarn-flink的快速部署&#xff08;单机部署&#xff09;。 环境&#xff1a;①操作系统&#xff1a;CentOS 7.6&#xff1b;②CPU&#xff1a;x86&#xff1b;③用户&#xff1a;root。 1.前置条件 把下面的的脚本保存到“pre-install.sh”文件&#x…

【vue】vue中插槽slot的用法详解

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

爬虫获取的数据如何用于市场分析?

在数字化时代&#xff0c;数据已成为企业决策的重要资产。通过爬虫技术获取的数据可以为市场分析提供丰富的原材料。本文将探讨如何利用Python爬虫获取的数据进行市场分析&#xff0c;并提供代码示例。 1. 数据收集 首先&#xff0c;我们需要通过爬虫收集相关数据。以电商行业…

Linux高阶——1123—服务器基础服务器设备服务器基础能力

目录 1、服务器基础 1、服务器基本概述 2、服务器设计之初解决的问题 网络穿透 网络数据设备间的收发 3、服务器的类型C/S、B/S 2、服务器设备 将自己的服务器软件部署上线 3、代理服务器负载均衡&#xff0c;以及地址绑定方式 4、服务器的基础能力 1、服务器基础 1…

DICOM图像深入解析:为何部分DR/CR图像默认显示为反色?

概述 在数字医学影像处理中,CR(Computed Radiography,计算机放射摄影)和DR(Digital Radiography,数字放射摄影)技术广泛应用于医疗影像获取与分析。然而,临床实践中常常遇到这样一个问题:部分CR/DR图像在默认打开时呈现为反色(即负片效果),需手动反色后才能正常阅片…

公网弹性绑定负载均衡收费吗?

公网弹性绑定负载均衡收费吗&#xff1f;公网弹性绑定负载均衡&#xff08;ELB&#xff09;是收费的。费用主要包括公网IP费、带宽费和负载均衡实例费。其中&#xff0c;带宽费可以按固定带宽或实际使用流量计费&#xff0c;而实例费则根据类型、规格和使用时长来定价。此外&am…

【ArcGISPro】根据yaml构建原始Pro的conda环境

使用场景 我们不小心把原始arcgispro-py3的conda环境破坏了,我们就可以使用以下方法进行修复 查找文件 在arcgis目录下找到yaml文件 如果没找到请复制以下内容到新的yaml文件 channels: - esri - defaults dependencies: - anyio=4.2.0=py311haa95532_0 - appdirs=1.4.4=p…

多头数(head number);d_model、d_k;词嵌入维度之间的关系;多头是对不同维度的特征分开提取,意义在于将并行执行

目录 多头是对不同维度的特征分开提取,意义在于将并行执行 之后的每头提取的特征仅仅进行矩阵拼接 多头数(head number) d_model、d_k 词嵌入维度之间的关系 词嵌入的维度(d_model)决定了权重矩阵的形状 一、概念解释 二、关系举例说明 多头数,权重矩阵的长度和词…

【Google Cloud】Private Service Connect 托管式服务

简介 Private Service Connect 是什么 Private Service Connect 是 Google Cloud&#xff08;原名 GCP&#xff09;Virtual Private Cloud&#xff08;VPC&#xff09;的一项功能。 该功能主要用于以下两个场景&#xff1a; 使用私有 IP 访问 Google Cloud 的 API。将用户自…

【redis 】string类型详解

string类型详解 一、string类型的概念二、string类型的常用指令2.1 SET2.2 GET2.3 MSET2.4 MGET2.5 SETNX2.6 INCR2.7 INCRBY2.8 DECR2.9 DECRBY2.10 INCRBYFLOAT2.11 APPEND2.12 GETRANGE2.13 SETRANGE2.14 STRLEN 三、string类型的命令小结四、string类型的内部编码五、strin…

跨平台应用开发框架(1)----Qt(组件篇)

目录 1.Qt 1.Qt 的主要特点 2.Qt的使用场景 3.Qt的版本 2.QtSDK 1.Qt SDK 的组成部分 2.安装 Qt SDK 3.Qt SDK 的优势 3.Qt初识 1.快速上手 widget.cpp mian.cpp widget.h Helloworld.pro 2.对象树 3.坐标系 4.信号和槽 1. 信号和槽的基本概念 2. 信号和槽的…

Element UI 打包探索【2】

目录 第三个命令 第四个命令 第五个命令 第六个命令 第七个命令 cross-env BABEL_ENV babel 第八个命令 总结 书&#x1f4da;接上文Element UI 打包探索【1】我们继续来看 第三个命令 "lint": "eslint src/**/* test/**/* packages/**/* build/**/* …

JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)

目录 JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能&#xff0c;JS中排序算法的使用详解&#xff08;附实际应用代码&#xff09; 一、为什么要使用Array.sort() 二、Array.sort() 的使用与技巧 1、基础语法 2、返回值 3、…

丹摩 | 利用 CogVideoX 生成视频

声明&#xff1a;非广告&#xff0c;纯用户体验 1. CogVideoX CogVideoX 是智谱 AI 推出的一款极具创新性与突破性的视频生成产品。它在技术层面展现出诸多卓越特性&#xff0c;例如其采用的 Diffusion Transformer&#xff08;DiT&#xff09;架构奠定了强大的生成能力基础…