asp.netcore(代码片段)

wewant wewant     2023-04-03     210

关键词:

1. 缓存

缓存指的是在软件应用运行过程中,将一些数据生成副本直接进行存取,而不是从原始源(数据库,业务逻辑计算等)读取数据,减少生成内容所需的工作,从而显著提高应用的性能和可伸缩性,使用好缓存技术,有利于提高我们提升用户体验性。

对于缓存的使用有以下一些注意点:

  • 缓存最适用于不常更改且生成成本很高的数据。
  • 代码应始终具有回退选项,以提取数据,而不依赖于可用的缓存值。
    我们应该以从不依赖于缓存数据的方式编写和测试应用。缓存是会失效的,我们在进行应用开发时应该考虑到缓存失效的情况,提供缓存失效时按照正常逻辑获取相关数据的方式。
  • 缓存使用短缺资源:内存。 我们应该限制缓存增长:
    • 不要将外部输入插入到缓存中。 例如,不建议使用用户提供的任意输入作为缓存键,因为输入可能会消耗不可预测的内存量。
    • 使用过期限制缓存增长。
    • 应当限制缓存的大小,避免缓存过度增长

软件开发中对缓存的使用一般有两种情况,一种是内存缓存,一种是分布式缓存。

2. NET Core 的内存缓存

内存缓存是最简单的一种缓存方式,就是使用应用所在的服务器的内存来保存一些数据副本,利用内存读写比磁盘、网络请求快的特点来提供应用性能。内存缓存一般应用于单机应用,一旦应用重启,内存缓存中的数据就会丢失。

如果是在服务器场(多个服务器)中运行的应用使用内存缓存,应确保在使用内存中缓存时会话是粘滞的。 粘滞会话可确保来自客户端的请求都转到同一服务器。

2. 1 内存缓存启用

.NET Core 框架下对于内存缓存的使用是通过 IMemoryCache ,可以通过将其注册到容器中,之后在需要的地方注入使用。对于大多数应用,在 Program.cs 中调用许多其他 AddService 方法可以启用 IMemoryCache,例如 AddMvc、AddControllersWithViews、AddRazorPages、AddMvcCore().AddRazorViewEngine 等 。

如果应用中没有使用到上述的这些方法,我们也可以自行引入 Microsoft.Extensions.Caching.Memory Nuget 包,通过 AddMemoryCache 方法启动内存缓存。为了方便演示,以下示例使用 .NET 6 控制台应用。

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Host.CreateDefaultBuilder(args)
	.ConfigureServices(services =>
	
		services.AddMemoryCache();
	)
	.Build().Run();

2. 2 内存缓存基本用法

内存缓存的使用,只需要将 IMemoryCache 服务注入到类中进行使用即可。我们可以通过 TryGetValue 方法尝试从缓存中获取数据,通过 Set 方法向缓存中添加数据。

public interface ICacheService

	public void PrintDateTimeNow();


public class CacheService : ICacheService

	public const string CacheKey = "CacheTime";
	private readonly IMemoryCache _cache;
	public CacheService(IMemoryCache memoryCache)
	
		_cache = memoryCache;
	

	public void PrintDateTimeNow()
	
		var time = DateTime.Now;
		if(!_cache.TryGetValue(CacheKey, out DateTime cacheValue))
		
			cacheValue = time;
			_cache.Set(CacheKey, cacheValue);
		
		time = cacheValue;

		Console.WriteLine("缓存时间:" + time.ToString("yyyy-MM-dd HH:mm:ss"));
		Console.WriteLine("当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
	


using CacheSample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var host = Host.CreateDefaultBuilder(args)
	.ConfigureServices(services =>
	
		services.AddMemoryCache();
		services.AddTransient<ICacheService, CacheService>();
	)
	.Build();

var service = host.Services.GetRequiredService<ICacheService>();
service.PrintDateTimeNow();

Task.Delay(TimeSpan.FromSeconds(2)).Wait();

service.PrintDateTimeNow();

host.Run();

通过控制台打印结果,可以看到,当前时间已经改变,但是缓存的时间是之前的数据。

缓存系统将缓存项存储为键值对,内存缓存中键和值都可以是任意类型,不过一般情况下我们会将字符串作为键。之后会讲到的分布式缓存中,则要求值必须是 byte[] 类型。

除此之外,还可以通过 GetOrCreate 或 GetOrCreateAsync 将获取和添加的操作结合。

public class CacheService : ICacheService

	public const string CacheKey = "CacheTime";
	private readonly IMemoryCache _cache;
	public CacheService(IMemoryCache memoryCache)
	
		_cache = memoryCache;
	

	public void PrintDateTimeNow()
	
		//var time = DateTime.Now;
		//if(!_cache.TryGetValue(CacheKey, out DateTime cacheValue))
		//
		//    cacheValue = time;
		//    _cache.Set(CacheKey, cacheValue);
		//
		//time = cacheValue;

		var time = _cache.GetOrCreate(CacheKey, cacheEntry =>
		
			return DateTime.Now;
		);

		Console.WriteLine("缓存时间:" + time.ToString("yyyy-MM-dd HH:mm:ss"));
		Console.WriteLine("当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
	

除了 TryGetValue 方法外,如果你确定缓存中一定存在相应的数据,还可以通过 Get 方法获取数据,Get 方法支持泛型,可以直接进行类型转换,但是如果缓存中不存在该缓存项,则会返回对应类型的默认值。

var timeCache = _cache.Get<DateTime>(CacheKey);![image]

2.3 缓存过期设置

一般情况下,我们会对缓存数据设置过期时间,一个是为将一些长期未被访问的缓存条目移除,避免缓存过度增长,一方面是为了更新数据,避免长时间的数据副本和源数据不一致。

.NET Core 下内存缓存的过期时间设置可以通过以下的方式:

(1) 通过 Set 方法设置

var time = DateTime.Now;
if (!_cache.TryGetValue(CacheKey, out DateTime cacheValue))

	cacheValue = time;
	// 设置绝对过期时间
	// 两种实现的功能是一样的,只是时间设置的方式不同而已
	// 传入的是 AbsoluteExpirationRelativeToNow, 相对当前的绝对过期时间,传入时间差,会根据当前时间算出绝对过期时间
	_cache.Set(CacheKey, cacheValue, TimeSpan.FromSeconds(2));
	// 传入的是 AbsoluteExpiration,绝对过期时间,传入一个DateTimeOffset对象,需要明确的指定具体的时间
	// _cache.Set(CacheKey, cacheValue, DateTimeOffset.Now.AddSeconds(2));

time = cacheValue;

调整一下入口文件的代码,如下:

var service = host.Services.GetRequiredService<ICacheService>();
service.PrintDateTimeNow();

Task.Delay(TimeSpan.FromSeconds(1)).Wait();
service.PrintDateTimeNow();

Task.Delay(TimeSpan.FromSeconds(2)).Wait();
service.PrintDateTimeNow();

可以看到,在第二次输出的时候,缓存没过期,时间是不变的,第三次的时候缓存过期了,时间改变了。

对于缓存过期时间的设置,除了绝对过期时间,还有缓动过期时间。滑动过期时间是指,如果在规定的过期时间内缓存有被再一次调用,过期时间就会重新更新,从头开始计算,每次被调用都会重新开始。

Set 方法没有直接的参数设置滑动过期时间,只能通过 MemoryCacheEntryOptions 对象设置,当然相对过期时间等其他配置也可以通过该对象设置。

var memoryCacheEntryOption = new MemoryCacheEntryOptions();
// 滑动过期时间是一个相对时间
memoryCacheEntryOption.SlidingExpiration = TimeSpan.FromSeconds(3);
_cache.Set(CacheKey, cacheValue, memoryCacheEntryOption);

可以看到,缓存时间一直没有变,因为虽然三次输出时间加起来超过了三秒,但是三次输出之间的间隔都没有超过3秒,而每调用一次缓存都会刷新超时时间,所以缓存一直没有过期。

(2) 通过 CreateEntry 方法设置

var time = DateTime.Now;
if (!_cache.TryGetValue(CacheKey, out DateTime cacheValue))

	cacheValue = time;
	var entry = _cache.CreateEntry(CacheKey);
	// 设置绝对过期时间
	// 两种实现的功能是一样的,只是时间设置的方式不同而已
	// 传入的是 AbsoluteExpirationRelativeToNow, 相对当前的绝对过期时间,传入时间差,会根据当前时间算出绝对过期时间
	entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(2);
	// 传入的是 AbsoluteExpiration,绝对过期时间,传入一个DateTimeOffset对象,需要明确的指定具体的时间
	entry.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(2);
	entry.Value = cacheValue;

time = cacheValue;

(3) 通过 GetOrCreate 或 GetOrCreateAsync 方法设置

var time = _cache.GetOrCreate(CacheKey, cacheEntry =>

	// 两种实现的功能是一样的,只是时间设置的方式不同而已
	// 相对当前的绝对过期时间,传入时间差,会根据当前时间算出绝对过期时间
	cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3);
	// 绝对过期时间,传入一个DateTimeOffset对象,需要明确的指定具体的时间
	// cacheEntry.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(2);
	// 滑动过期时间是一个相对时间
	cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
	return DateTime.Now;
);

GetOrCreateGetOrCreateAsync 方法的回调参数其实就是实现了 ICacheEntry 接口的对象。

这里同时设置了绝对过期时间和滑动过期时间,对于一个缓存项,仅具有滑动过期时间的缓存项集有永不过期的风险。 如果在滑动过期间隔内重复访问缓存项,则该项永远不会过期。 将滑动过期与绝对过期相结合,以确保项目过期。 绝对过期时间设置一个上限,即在滑动过期间隔内未请求该项时,仍允许该项提前过期的时间。 如果经过了可调到期间隔或绝对到期时间,则会从缓存中逐出项。

可以看到输出结果如下:

第三次调用的时候,时间改变了,这是因为输入滑动过期时间一直在更新,但是绝对过期时间超过了,所以缓存失效了。

这一篇就先到这里,后面还有内容,但是考虑到如果全塞在一篇里面的话,这篇文章就太长了,大家的阅读体验会不大好,所以就拆成两篇了。



参考文章:
ASP.NET Core 中的内存中缓存



ASP.NET Core 系列:

目录:ASP.NET Core 系列总结
上一篇:ASP.NET Core - 选项系统之源码介绍

你真的了解asp.netcore部署模型吗?(代码片段)

原文:你真的了解ASP.NETCore部署模型吗? ----------------------------   以下内容针对ASP.NETCore2.1,2.2出现IIS进程内寄宿暂不展开讨论--------------------------      相比ASP.NET,ASP.NETCore 查看详情

asp.netcore入门教程1使用asp.netcore构建第一个web应用(代码片段)

原文:ASP.NETCore入门教程1、使用ASP.NETCore构建第一个Web应用一、前言1、本文主要内容VisualStudioCode开发环境配置使用ASP.NETCore构建Web应用ASP.NETCoreWeb应用启动类说明ASP.NETCoreWeb项目结构说明2、本教程环境信息软件/环境说明操作系统Wi... 查看详情

3.netcore笔试题(代码片段)

1.什么是ASP.NETCore?2.ASP.NETCore中AOP的支持有哪些?3.ASP.NETCoreFilter的注册方式有哪些?4.ASP.NETCoreFilter如何支持依赖注入?5.ASP.NETCore如何和读取配置文件中的内容?6.ASP.NETCore有哪些好的功能?7.ASP.NETCore跟ASP.NET比较有哪些更好的地方... 查看详情

理解asp.netcore:处理管道(代码片段)

理解ASP.NETCore处理管道在ASP.NETCore的管道处理部分,实现思想已经不是传统的面向对象模式,而是切换到了函数式编程模式。这导致代码的逻辑大大简化,但是,对于熟悉面向对象编程,而不是函数式编程思路的开发者来说,是... 查看详情

详解asp.netcore中的cookies(代码片段)

目录详解Asp.NetCore中的cookies搞懂cookiesAsp.Net中cookies的实现从http中获取cookies将cookies写入http中总结及感想详解Asp.NetCore中的cookies搞懂cookies我之前写过一篇文章来介绍cookies,如果你对cookies不是很了解请移步理解cookies这篇文章,这... 查看详情

asp.netcore简单介绍(代码片段)

  Asp.netcore是一个开源和跨平台的框架,用于构建如WEB应用,物联网(IoT)应用和移动后端应用等连接到互联网的基于云的现代应用程序。asp.netcore应用可运行.net和。netframework之上。  它由最小开销的模块化的组件构成,因... 查看详情

[asp.netcore]asp.netcore与配置系统的集成(代码片段)

Asp.netCore默认添加的配置提供者1)加载现有的Iconfiguration2)加载项目根目录下的appsettings.json3)加载项目根目录下的appsettings.Enviroment4)当程序运行在开发环境下,程序会加载“用户机密”配置5)加载环境变量中的配置6)加载命令行... 查看详情

[asp.netcore]asp.netcore与配置系统的集成(代码片段)

Asp.netCore默认添加的配置提供者1)加载现有的Iconfiguration2)加载项目根目录下的appsettings.json3)加载项目根目录下的appsettings.Enviroment4)当程序运行在开发环境下,程序会加载“用户机密”配置5)加载环境变量中的配置6)加载命令行... 查看详情

asp.netcore入门教程1使用asp.netcore构建第一个web应用(代码片段)

...、前言1、本文主要内容VisualStudioCode开发环境配置使用ASP.NETCore构建Web应用ASP.NETCoreWeb应用启动类说明ASP.NETCoreWeb项目结构说明2、本教程环境信息软件/环境说明操作系统Windows10SDK2.1.401ASP.NETCore2.1.3IDEVisualStudioCode1.27浏览器Chrome693、... 查看详情

一个mini的asp.netcore框架的实现(代码片段)

原文:一个Mini的ASP.NETCore框架的实现一、ASP.NETCoreMini  在2019年1月的微软技术(苏州)俱乐部成立大会上,蒋金楠老师(大内老A)分享了一个名为“ASP.NETCore框架揭秘”的课程,他用不到200行的代码实现了一个ASP.NETCoreMini... 查看详情

asp.netcore定时任务保持存活,不会被回收。asp.netcore回收事件(代码片段)

核心类,继承IHostedService,处理启动和退出方法publicinterfaceIHostedService //启动程序时触发TaskStartAsync(CancellationTokencancellationToken); //关闭时触发。IIS应用程序池回收时触发TaskStopAsync(CancellationTokencancellatio 查看详情

[六]asp.netcore开发人员异常页面和环境变量(代码片段)

开发人员异常页面和环境变量一、ASP.NETCore开发人员异常页面1、UseDeveloperExceptionPage中间件二、ASP.NETCore中的环境变量1、软件开发环境:2、配置ASPNETCORE_ENVIRONMENT变量3、访问ASPNETCORE_ENVIRONMENT变量值一、ASP.NETCore开发人员异常页... 查看详情

[十一]asp.netcore中的taghelper(代码片段)

Taghelper一、ASP.NETCore中的Taghelper(1)导入内置TagHelpers(2)使用Taghelper生成链接(3)为什么要使用Taghelper(标记助手)(4)ASP.NETCoreImage标记助手(TagHelper)(5)A 查看详情

一步步完成“迷你版”的asp.netcore框架(代码片段)

一前言Artech分享了200行代码,7个对象——让你了解ASP.NETCore框架的本质。用一个极简的模拟框架阐述了ASP.NETCore框架最为核心的部分。这里一步步来完成这个迷你框架。二先来一段简单的代码这段代码非常简单,启动服务器并监... 查看详情

asp.netcore获取客户端ip地址(代码片段)

1、在ConfigureServices注入IHttpContextAccessor//ASP.NETCore2.1的注入方式//services.AddHttpContextAccessor();//services.TryAddSingleton<IActionContextAccessor,ActionContextAccessor>();//注入services.AddSing 查看详情

asp.netcore2.0:八.图说管道(代码片段)

  本文通过一张GIF动图来继续聊一下ASP.NETCore的请求处理管道,从管道的配置、构建以及请求处理流程等方面做一下详细的研究。(ASP.NETCore系列目录)一、概述  上文说到,请求是经过Server监听=>处理成httpContext=>Applicat... 查看详情

asp.netcore中间件基本用法(代码片段)

ASP.NETCore中间件ASP.NETCore的处理流程是一个管道,而中间件是装配到管道中的用于处理请求和响应的组件。中间件按照装配的先后顺序执行,并决定是否进入下一个组件。中间件管道的处理流程如下图(图片来源于官网):管道... 查看详情

asp.netcore依赖注入最佳实践——提示与技巧(代码片段)

在这篇文章,我将分享一些在ASP.NETCore程序中使用依赖注入的个人经验和建议。这些原则背后的动机如下:高效地设计服务和它们的依赖。预防多线程问题。预防内存泄漏。预防潜在的BUG。这篇文章假设你已经基本熟悉依赖注入... 查看详情