.NET Core中间件与依赖注入的一些思考

点击上方蓝字"小黑在哪里"关注我吧

1.起源?

为什么会有这篇文章呢? 源于我看了老A的aspnet core 3 框架揭秘[1] 请求管道 篇产生的疑惑?

三点疑惑:

  1. Singleton服务中注入Scoped服务产生内存泄露?

  2. 关于中间件的生命周期是Singleton的?

  3. 怎么避免中间件、Singleton服务中使用Scoped服务不产生内存泄漏?

2.知识面覆盖

示例中会覆盖到aspnet core相关的配置、依赖注入(周期)、中间件的知识点,若不清楚的需要先看看这些概念以及基本使用。

收获:和我一起带着以上三个问题来进行验证也就会收获到相关知识点。

3. 测试环境准备

创建三个服务:

1. IOrderAppService(singleton)
2. IProductAppService(scoped)
3. ITransientTestAppService(transient)

创建请求控制器:

public class ProductController : Microsoft.AspNetCore.Mvc.Controller
{private int time = 1;private readonly IHostApplicationLifetime _lifetime;public ProductController(IProductAppService productAppService1,IProductAppService productAppService2,IOrderAppService orderAppService1,IOrderAppService orderAppService2,ITransientTestAppService transientTestAppService1,ITransientTestAppService transientTestAppService2,IHostApplicationLifetime lifetime){_lifetime = lifetime;}[HttpGet][Route("/get")]public Task<string> Get(){return Task.FromResult($"第{time++}次请求成功!");}[HttpGet][Route("/stop")]public void Stop() => _lifetime.StopApplication();
}

创建中间件:

public sealed class UrlMiddleware
{private int times = 1;private readonly RequestDelegate _next;public UrlMiddleware(RequestDelegate next,IProductAppService productAppService,ITransientTestAppService transientTestAppService){//构造中的productAppService服务是由IApplicationBuilder.ApplicationServices根容器创建的_next = next;}public async Task InvokeAsync(HttpContext context,IProductAppService productAppService,ITransientTestAppService transientTestAppService){//invoke中的productAppService其实是context.RequestServices子容器创建的。//这里的context.RequestServices子容器也是由IApplicationBuilder.ApplicationServices根容器创建来的。var productService = context.RequestServices.GetRequiredService<IProductAppService>();//使用解析的方式和上面方法中注入进来是一样的作用,切记是使用子容器RequestServices解析Console.WriteLine($"请求第{times++}次进入UrlMiddleware中间件。hash:{this.GetHashCode()}");await _next(context);}
}

注册服务:

service.AddTransient<ITransientTestAppService, TransientTestAppService>().AddScoped<IProductAppService, ProductAppService>().AddSingleton<IOrderAppService, OrderAppService>();

这里若使用的IMiddleware创建中间件也记得需要注册。

4.开始验证

4.1 关于中间件的生命周期是Singleton的?

这里我们先验证下这个问题。为第一个问题做铺垫。

文章中我就不做过多的代码介绍,主要是对代码片段的解释,有需要的可以看源代码[2]

  • 开始运行:

    dotnet run
    

    会注意到中间件构造中注入的服务会在项目启动完成前就会创建完成。

  • 开始请求:

    输入http://localhost:5002/get, 这是因为配置了 UseUrls,也可以直接使用UseSetting("urls"")

    使用UseSetting的key默认定义在WebHostDefaultsHostDefaults

    为了验证问题我们请求两次。

    开始请求
    中间件是否是单例

    分析总结:从两次请求中可以确定不管是强类型的中间件还是按照约定(弱类型)的中间件都是单例的(Singleton)

    这里穿插一下关于Singleton\Scoped\Transient生命周期控制台输出:分析总结:

    • Scoped服务请求中只会创建一次并且请求完成后释放

    • Transient服务每一次都会重新创建并且请求完成后全部释放

    • Singleton整个应用程序周期内只会创建一次并且直到应用程序关闭时才会释放(慎用慎选择)

4.2 Singleton服务中注入Scoped服务产生内存泄露?

调用http://localhost:5002/stop 进行远程关闭应用程序。控制台输出:分析总结:中间件构造中注入scoped服务时会跟随根容器的释放才会释放,相当于说就是会在整个应用程序生命周期中存在,所以也就容易导致内存泄漏

从这里还没能表现出构造中的服务和invoke方法中的服务区别。。。下面进行验证:

分析总结:

从图中画线中能看出请求完成后只有invoke方法中的scoped\transient服务释放了,中间件构造中的任何类型服务都不会得到释放,所以需要在中间件使用
关于非singleten服务时在方法中进行注入,不要使用构造注入,这是为什么呢?其实invoke方法中的服务是通过子容器(context.RequestServices)创建而来的,所以跟随请求完成子容器释放也就会释放掉子容器内创建出的服务。
context.RequestServices是由IApplicationBuilder.ApplicationServices根容器创建而来的。以上内容也只是使用中间件这种特殊来进行了测试,那么怎么来验证Singleton服务中注入scoped来进行验证呢?自行尝试?应该是不可以的哦?`ServiceProviderOptions`。

4.3 怎么避免中间件、Singleton服务中使用Scoped服务不产生内存泄漏?

其实4.2中已经有了答案了。

如何避免?

在中间件中使用invoke方法中注入服务或者使用context.RequestServices.GetRequiredService<>();来解析服务,不推荐(反模式)。

在singleton服务中使用使用IServiceProvider来创建子容器解析。

要是以上内容有什么不对的地方欢迎也希望得到指点。

5 总结

从自己看书到自己写代码来验证以及写这篇文章多多少少算花了两天的时间,但是感觉还是有收获的,算是搞清楚了一些问题。

强烈推荐老A的 aspnet core 3 框架揭秘[3] ,对深入aspnet core有很大的帮助,能 够对aspnet core中的知识点有一个大体轮廓。

参考资料

[1]

aspnet core 3 框架揭秘: https://www.cnblogs.com/artech/

[2]

源代码: https://github.com/jonny-xhl/my-demo/tree/master/src/middleware/Jonny.AllDemo.SingleMiddleware

[3]

aspnet core 3 框架揭秘: https://www.cnblogs.com/artech/

如果本文对您有用,

不妨点个“”或者转发朋友圈支持一下

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

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

相关文章

深度学习入门笔记(9)—— Regularization to avoid overfitting 用正则化来避免过拟合

在维基百科的词条中&#xff0c;正则化就是让答案变得更简单的&#xff0c;目的是防止过拟合。在分类上可以分为显式正则化&#xff08;对目标函数添加先验、惩罚、约束、成本&#xff09;和隐式正则化&#xff08;所有其他形式的正则化如提前停止、集成学习、Dropout 等&#…

蓝桥杯-填空题-购物单

一&#xff1a;题目 **** 180.90 88折 **** 10.25 65折 **** 56.14 9折 **** 104.65 9折 **** 100.30 88折 **** 297.15 半价 **** 26.75 65折 **** 130.62 半价 **** 240.28 …

C# 中 System.Index 结构体和 Hat 运算符(^)的全新用法

翻译自 John Demetriou 2019年2月17日 的文章 《C# 8 – Introducing Index Struct And A Brand New Usage For The Hat Operator》今天我们要讲的是 Hat 运算符(^)。目前为止&#xff0c;Hat 运算符(^)已经被用作布尔类型的异或运算符&#xff0c;以及字节、整型类型的按位异或…

Lagrange Multipliers 拉格朗日乘数法(含 KKT 条件)

最优化问题通常是指对于给定的某一函数&#xff0c;求其在指定作用域上的全局最小值&#xff0c;一般情况下&#xff0c;最优化问题一般分为三种情况&#xff1a; &#xff08;1&#xff09;无约束条件 对于无约束条件的优化问题中&#xff0c;如果一个函数 f 是凸函数&#…

蓝桥杯-代码-数字三角形

一:题目 二:代码 #include <iostream> #include<vector> using namespace std; int main() {/**思路:1.确定dp数组的定义以及下标的含义dp[i][j] 表示的是一条路径到达下标i和j时&#xff0c;的最大值 2.确定dp数组的状态转移公式我们当前位置的最大值其实是由右上…

ABP VNext从单体切换到微服务

注&#xff1a;此处的微服务只考虑服务部分&#xff0c;不考虑内外层网关、认证等。ABP VNext从单体切换到微服务&#xff0c;提供了相当大的便利性&#xff0c;对于各模块内部不要做任何调整&#xff0c;仅需要调整承载体即可。ABP can help you in that point by offerring a…

dotnet 在 UOS 国产系统上使用 Xamarin Forms 创建 xaml 界面的 GTK 应用

在前面几篇博客告诉大家如何部署 GTK 应用&#xff0c;此时的应用是特别弱的&#xff0c;大概只是到拖控件级。尽管和 WinForms 一样也能写出特别强大的应用&#xff0c;但是为了提升一点开发效率&#xff0c;咱开始使用 xaml 神器写界面。本文告诉大家如何在 UOS 国产系统上&a…

赛码-编程题-打字

一:题目 二&#xff1a;上码 #include<bits/stdc.h> using namespace std;int main() {int n;vector<int> v1;cin >> n;for(int i 0; i < n; i) {string str;int count 0;cin >> str;for (int j 0; j < str.size(); j) {int num int(str[j]…

Python 中的 with 语句用法和 Pytorch 中的 with torch.no_grad() 解析

Python 中的 with 语句适用于对资源进行访问的场合&#xff0c;确保不管使用过程中是否发生异常都会执行必要的“清理”操作&#xff08;异常处理&#xff09;&#xff0c;释放资源&#xff0c;比如文件使用后自动关闭&#xff0f;线程中锁的自动获取和释放等。例如下面是文件读…

遍历 Dictionary,你会几种方式?

一&#xff1a;背景 1. 讲故事昨天在 StackOverflow 上看到一个很有趣的问题&#xff0c;说: 你会几种遍历字典的方式&#xff0c;然后跟帖就是各种奇葩的回答&#xff0c;挺有意思&#xff0c;马上就要国庆了&#xff0c;娱乐娱乐吧&#xff0c;说说这种挺无聊的问题?????…

PyTorch 中各种操纵维度的函数比较 view() reshape() squeeze() unsqueeze() flatten()

首先&#xff0c;假设我们有一个三行四列的张量 X&#xff1a; view() 和 reshape() 函数都可以指定并改变张量的维度&#xff0c;它们本质上是相同的&#xff0c;只有两点区别&#xff1a; 1、view() 函数返回的是原始张量的视图&#xff0c;而 reshape() 函数返回的是原始张…

.NET Core 下的 API 网关

网关介绍网关其实就是将我们写好的API全部放在一个统一的地址暴露在公网&#xff0c;提供访问的一个入口。在 .NET Core下可以使用Ocelot来帮助我们很方便的接入API 网关。与之类似的库还有ProxyKit&#xff0c;微软也发布了一个反向代理的库YARP。关于网关的介绍不多说了&…

leetcode剑指 Offer 29. 顺时针打印矩阵

一&#xff1a;题目 二:上码 class Solution { public:vector<int> spiralOrder(vector<vector<int>>& matrix) {//判空处理 否则会出现空指针异常if(matrix.size() 0 || matrix[0].size() 0){return {};}int m matrix.size();// 行int n matrix[0].…

VS也可以这样进行快捷安装

前言记录带新人的那些事&#xff0c;主要是一些工作技巧上的分享部门老大&#xff1a;阿元&#xff0c;明天有几个实习生新人开发来入职&#xff0c;你负责带一下他们阿元&#xff1a;明天吗&#xff1f;哦&#xff0c;好的&#xff0c;顺便问下&#xff1a;男的女的&#xff1…

蓝桥杯-卡片-填空题

一:题目 二:思路 1是最快消耗完的计算1的个数即可 三&#xff1a;上码 #include <iostream> using namespace std; int main() {int ans 0;int count 0;for(int i 1; i < 20000; i) {string str to_string(i);// cout << str << endl;for(int j …

跟我一起学.NetCore之Swagger让前后端不再烦恼及界面自定义

前言随着前后端分离开发模式的流行&#xff0c;接口对接、联调成为常事&#xff0c;前端同事会经常问&#xff1a;我需要调哪个接口&#xff1f;这个接口数据格式是啥&#xff1f;条件都传啥&#xff1f; 对于一些紧急接口可能会采取沟通对接&#xff0c;然后补文档&#xff0c…

由浅入深,带你搞懂 Pytorch 中的张量 tensor 是什么

目录1、tensor 是什么&#xff1f;2、tensor 的三个属性2.1 Rank 秩2.2 Axis&#xff08;复数 为 Axes&#xff09; 轴2.3 Shape 形状3、Pytorch 中 torch.Tensor 的三个属性3.1 torch.dtype3.2 torch.device3.3 torch.layout4、创建张量的两种方法4.1 从现有数据创建张量4.2 凭…

蓝桥杯-排序-填空题

一:题目 二:上码 #include <iostream> using namespace std; int main() {// 请在此输入您的代码/**/**冒泡排序中:我们考虑到最坏的情况,那就是全都是逆序 那么就需要交换 N(N-1)/2; 那么100次 最起码需要 15个字符&#xff0c;而15个字符完全逆序的话 需要交换 105次…

打造钉钉事件分发平台之钉钉审批等事件处理

前言上讲和上上讲我们说到了钉钉的审批和钉钉通讯录的一个简单示例&#xff0c;这次我们讲下如何快速打造一个自己的钉钉事件分发平台。让你能够通过监听用户在钉钉上的操作&#xff0c;然后进行对应的业务处理&#xff0c;比如钉钉流程审批完后业务处理、通讯录员工增加后对应…

Pytorch 中 Dataset 和 DataLoader,以及 torchvision 的 datasets 完全理解

目录1、torch.utils.data.Dataset()2、torch.utils.data.Sampler()3、torch.utils.data.DataLoader()4、torchvision.datasets.ImageFolder()5、例子 torchvision.datasets.FashionMNIST()1、torch.utils.data.Dataset() 首先最基础的&#xff0c;是 torch.utils.data.Dataset…