ASP.NET Core 依赖注入基础测试题

AddSingleton,AddScoped,AddTransient 三者的差异性测试 服务类型 同一个 HTTP 请求的范围 多个不同 HTTP 请求 Singleton Service 同一个实例 同一个实例 Scoped Service 同一个实例 新实例 Transient Service 新实例 新实例 自己理解的测试代码:... 阅读详情

作为一名 ASP.NET Core 的开发者,依赖注入可以说是居家旅行开发调试的必备技能。
在这篇文章里,希望通过一些常识性测试题,来巩固学习一下依赖注入的基础知识。

作用域

请问下面这段代码的执行结果是什么?

public interface IServiceA { }

class ServiceA : IServiceA
{
    ServiceA()
    {
        Console.WriteLine("New SA");
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<IServiceA, ServiceA>();
        ...
    }
}

结果是报错:

System.AggregateException: 'Some services are not able to be constructed'
A suitable constructor for type 'AspNetCore.Services.ServiceA' could not be located.
Ensure the type is concrete and services are registered for all parameters of a public constructor.

官方文档在 Constructor injection behavior 有提过,如果通过构造函数注入,构造函数必须是 public 级别。

为什么 constructor 要 public 呢?因为默认的访问级别是 private。依赖注入是由 ASP.NET Core 实现的,自然是无法访问 private 级别的构造方法的。

那 class 需不需要是 public 呢?不需要,因为通过方法调用的方式已经让 DI 获取到了 class,如果是 using namespace 的情况下访问 class,才需要 class 也是 public。

生命周期

下面这段代码中,singleton 的 IServiceA 被 HelloController 所依赖,在项目启动之后,没有访问网页的情况下,ServiceA 会被初始化吗?

public interface IServiceA { }

public class ServiceA : IServiceA
{
    public ServiceA()
    {
        Console.WriteLine("New SA");
    }
}
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IServiceA, ServiceA>();
        ...
    }
}
public class HelloController : ControllerBase
{
    public WeatherForecastController(IServiceA sa)
    {
        Console.WriteLine($"Test Controller: {sa.GetType()}");
    }
}

ServiceA 并不会被初始化。DI 虽然会检查是否存在 public constructor ,但是不会立即初始化服务实例,只有在服务被使用的时候才会根据注册时的生命周期做初始化。ServiceA 只被 controller 依赖,而 controller 只有在请求过来的时候才会被初始化:
在这里插入图片描述
所以 ServiceA 也只有在请求到达 controller 的时候才会跟着 controller 一起被初始化。

如果连续访问三次 controller,会看到 singleton 在第一次请求到达时被初始化,后面传入的都还是以前的实例:

New SA
Test Controller: AspNetCore.Services.ServiceA
Test Controller: AspNetCore.Services.ServiceA
Test Controller: AspNetCore.Services.ServiceA

如果我们用 AddScoped 或者 AddTrancient,每次访问 API 都会看到 ServiceA 被初始化了:

New SA
Test Controller: AspNetCore.Services.ServiceA
New SA
Test Controller: AspNetCore.Services.ServiceA
New SA
Test Controller: AspNetCore.Services.ServiceA

依赖后的生命周期

如果 ServiceA 是 transient 的,ServiceB 是 singleton 的,ServiceB 和 controller 都依赖 ServiceA,请问第一次访问 controller 的路由,ServiceA 会被初始化几次?第二次访问呢?

public interface IServiceA { }
public class ServiceA : IServiceA
{
    public ServiceA()
    {
        Console.WriteLine("New SA");
    }
}

public interface IServiceB { }
public class ServiceB : IServiceB
{
    public ServiceB(IServiceA sa)
    {
        Console.WriteLine("New SB");
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<IServiceA, ServiceA>();
        services.AddSingleton<IServiceB, ServiceBz>();
        ...
    }
}
public class HelloController : ControllerBase
{
    public WeatherForecastController(IServiceA sa, IServiceB sb)
    {
        Console.WriteLine($"Test Controller: {sa.GetType()} {sb.GetType()}");
    }
}

第一次访问输出结果:

New SA
New SA
New SB
Test Controller: AspNetCore.Services.ServiceA AspNetCore.Services.ServiceB
New SA
Test Controller: AspNetCore.Services.ServiceA AspNetCore.Services.ServiceB

可以看到,ServiceA 因为是 transient 的,所以每次请求都会被初始化一次。而 ServiceB 是 singleton 的,虽然它依赖一个 transient 的 ServiceA,但是初始化之后就不会再传入新的 ServiceA 了,在 singleton 的 ServiceB 中的 ServiceA 也是 singleton 的。

如果在 transient 的 ServiceA 中依赖一个 singleton 的 ServiceB 呢?

New SB
New SA
Test Controller: AspNetCore.Services.ServiceA AspNetCore.Services.ServiceB
New SA
Test Controller: AspNetCore.Services.ServiceA AspNetCore.Services.ServiceB

singleton 的 ServiceB 不管在哪里取出,都是 singleton 的,虽然 ServiceA 和 controller 在多个请求中做了多次初始化,但是传入的都是同一个 ServiceB 实例。

多个依赖的初始化顺序

如果注册的时候是先 A 后 B,constructor 里是先 B 后 A,哪个会先被初始化?

public interface IServiceA { }
public class ServiceA : IServiceA
{
    public ServiceA()
    {
        Console.WriteLine("New SA");
    }
}

public interface IServiceB { }
public class ServiceB : IServiceB
{
    public ServiceB()
    {
        Console.WriteLine("New SB");
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IServiceA, ServiceA>();
        services.AddSingleton<IServiceB, ServiceB>();
        ...
    }
}

public class HelloController : ControllerBase
{
    public WeatherForecastController(IServiceB sb, IServiceA sa)
    {
        Console.WriteLine($"Test Controller: {sa.GetType()}");
    }
}

输出结果:

New SB
New SA
Test Controller: AspNetCore.Services.ServiceA AspNetCore.Services.ServiceB

虽然注入依赖的顺序是 AB ,但是因为调用顺序是 BA,所以会先初始化 B 再初始化 A

如果 B 的构造函数依赖了 A 呢?

public class ServiceB : IServiceB
{
    public ServiceB(IServiceA sa)
    {
        Console.WriteLine($"New SB with sa:{sa.GetType()}");
    }
}

输出结果:

New SA
New SB with sa:AspNetCore.Services.ServiceA
Test Controller: AspNetCore.Services.ServiceA AspNetCore.Services.ServiceB

此时会先把被依赖的 ServiceA 初始化完成再继续初始化 ServiceB。

如果依赖注入的时候是先注入 B 再注入 A 呢?

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IServiceB, ServiceB>();
    services.AddScoped<IServiceA, ServiceA>();
}

输出结果:

New SA
New SB with sa:AspNetCore.Services.ServiceA
Test Controller: AspNetCore.Services.ServiceA AspNetCore.Services.ServiceB

依赖注入的声明顺序并不重要,DI Container 会存储下 interface 和 class 的映射关系,在初始化的时候会根据依赖关系妥善处理。

一个接口多种实现

如果一个 interface 有多个实现类,并且都进行了注入,在 constructor 取出这个 interface 的时候会取到哪一个?多个实现类是否都会被初始化?

public interface IServiceA { }
public class ServiceA : IServiceA
{
    public ServiceA()
    {
        Console.WriteLine("New SA");
    }
}
public class ServiceB : IServiceA
{
    public ServiceB()
    {
        Console.WriteLine("New SB");
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IServiceA, ServiceA>();
        services.AddSingleton<IServiceA, ServiceB>();
        ...
    }
}

public class HelloController : ControllerBase
{
    public WeatherForecastController(IServiceA sa)
    {
        Console.WriteLine($"Test Controller: {sa.GetType()}");
    }
}

输出结果:

New SB
Test Controller: AspNetCore.Services.ServiceB

一个接口多个实现,只会取出最后的一个实现来构造实例。其他实现类的构造方法不会被调用。DI Container 在存好 interface 和 class 的映射关系后,如果有新的实现就会覆盖掉前面的映射。

多个接口一个实现

如果一个接口有多个实现,并且都进行了单例的依赖注入,在取出实例的时候会被初始化几次?

public interface IServiceA { }
public interface IServiceB { }
public class ServiceB : IServiceA, IServiceB
{
    public ServiceB()
    {
        Console.WriteLine("New SB");
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IServiceA, ServiceB>();
        services.AddSingleton<IServiceB, ServiceB>();
        ...
    }
}

public class HelloController : ControllerBase
{
    public WeatherForecastController(IServiceA sa, IServiceB sb)
    {
        Console.WriteLine($"Test Controller: {sa.GetType()} {sb.GetType()}");
    }
}

输出结果:

New SB
New SB
Test Controller: AspNetCore.Services.ServiceB AspNetCore.Services.ServiceB

可以看到,AddSingleton 是针对 interface 的单例,而不是实现类的单例。对于 DI 来说,ServiceB 是对两种 interface 的实现类,会分别进行初始化。

后续

这些问题都是比较基础的依赖注入问题,希望对于依赖注入的学习起到抛砖引玉的作用。其中的一些理解分析也只是个人观点,如果有错误的地方欢迎指出。

相关推荐

MT9V034摄像头学习笔记(一)_mt9v034摄像头循迹代码(1)

函数名称:uint8_t SCB_RegRead(uint8_t Device,uint8_t Address,uint16_t *Data)* 函数名称:uint8_t SCB_RegWrite(uint8 Device,uint8 Address,uint16 Data)* 函数名称:uint8 SCCB_SendByte(uint8 Data)* 函数名称:uint8 SCCB_ReadByte(void)* 修改时间:2018年3月4日。* 修改时间:2018年3月4日。

2401_87555661的博客 907

A suitable constructor for type ‘Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware‘

System.InvalidOperationException:“A suitable constructor for type ‘Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware’ could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.” 出现这样的错误 一

kso的博客 1424

AD24层次原理图设计避坑指南:如何解决端口连接与自动交叉引用问题

本文深入解析AD24层次原理图设计的核心机制,聚焦端口连接错误、自动交叉引用干扰等常见问题。通过对比自上而下与自下而上设计流程,提供端口类型匹配、命名规范等实用解决方案,并指导如何关闭烦人的红色框线视觉干扰,帮助工程师高效避坑,实现复杂系统的模块化设计。

wdx01234567的博客 615

asp.net 依赖注入_ASP.NET中的依赖注入练习:简介

asp.net 依赖注入 在本文中,我们将做一些练习来介绍ASP.NET中DI(依赖注入)的基础知识。 辅助功能级别 以下代码的结果是什么? public interface IServiceA { } class ServiceA : IServiceA { ServiceA() { Console.WriteLine( "New SA" ); ...

编程故事的地方 1665

RT-Thread学习笔记(5):SPI驱动框架分析

1、两个设备模型 在spi驱框架中,一般会自己初始化一个总线设备(rt_spi_bus),当我们通过挂载后,会相应注册一个从设备(rt_spi_device)。 1、总线设备模型 struct rt_spi_bus { struct rt_device parent; rt_uint8_t mode; const struct rt_spi_ops *ops; struct rt_mutex lock; struct rt_spi_device *owner; };

BLBQ962464的博客 1万+

数理统计四大分布---正态分布、卡方分布、学生t分布和F分布

在统计学上,我们会遇到一些常见的分布,除了正态分布外,,如t检验对应的t分布,检验对应的分布,方差分析对应的F分布等。这些分布是统计学的基础,在假设检验、方差分析等领域都起着至关重要的作用。在此,我们对这四种分布做详细的介绍,并附上MATLAB代码。 四种分布的讲解摘自MATLAB 2016的统计学和机器学习工具箱,有兴趣的可以去查看英文原版。 另外,为了方便,上传文档为图片格式,可能有些模糊...

suncangan的博客 5万+

关于springmvc中jackson的No suitable constructor found for type错误

启需要调用个第三方rest接口。 偷了个懒把json对象定义在了文件内,没有单独建立一个外部类而是做了个内部类。 结果就是不停地报"No suitable constructor found for type"。 承不是没有无参构造函数; 不是因为内部类被设置为private; 将内部类原模原样拷出来单独建立一个外部类就一切ok了,不然就是报一万年上述错误。 仅作现象记录,暂不去分析原由,总之忒奇怪

OHRadiance的专栏 6356

人工智能机器学习从入门到高级学习资料(含资料下载地址)

机器学习是一个技术含量较高的领域,高效学习需要多种途径相结合.以下是一些推荐的学习资源。首先介绍几个比较有用的社区,包含了丰富的学习资料

2401_86900375的博客 1644

JSON parse error: No suitable constructor found for type

错误信息: 2019-02-19 09:17:58,678 [http-nio-8080-exec-1] WARN org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - Failed to read HTTP message: org.springframework.http.converter...

weixin_30622107的博客 502

迈德威视相机调用( 基于 Linux系统+ OpenCV )

迈德威视相机调用( 基于 Linux系统+ OpenCV )前言一、登录迈德威视官网,下载官方SDK二、代码调用与视频帧处理( 基于 OpenCV )总结 前言     此博客记录了迈德威视工业相机在 Linux 系统下的代码调用及其环境配置,通过 OpenCV 对视频帧进行处理。   一、登录迈德威视官网,下载官方SDK      迈德威视官网 : http://www.mindvision.com.cn/     1. 进入官网后,在服务支持中选择软件下载。    2. 选择Linux系统S.

kkkkkk0729的博客 5674

在windows10/11中使用ipatool提取苹果app安装包ipa文件

本文介绍了在Windows10/11系统中使用ipatool工具下载苹果App安装包(.ipa)的详细步骤。首先需准备Windows电脑、AppleID账号并下载ipatool工具;具体操作包括:登录AppleID、搜索应用、获取购买授权、查看历史版本及下载指定版本。关键步骤强调获取授权的重要性,并提供了常用命令速查表,帮助用户完成从登录到下载的全过程。该教程适用于需要获取旧版本App或进行应用测试的场景。

zhengshaolin128的博客 2566

JsonMappingException: No suitable constructor found for type

JsonMappingException: No suitable constructor found for type使用SpringMVC接收客户端传过来的JSON串就报上面的错误提示。有点纳闷的是我之前有做过单元测试是没有问题的。不过我这个类是包含3个类的:public class Outer { public /*static*/ class A { } public

Yong Ledo的博客 1568

Chrome DevTools MCP 实战完整教程

本教程介绍了Chrome DevTools MCP(Model Context Protocol)在智能Web测试中的应用。该系统通过MCP协议将Chrome开发者工具能力标准化,并与AI结合,提供了统一的测试解决方案。核心功能包括智能自动化、深度调试分析和性能监控优化。教程详细讲解了环境搭建、MCP服务器配置、参数说明及多种工具集(输入自动化、导航、设备仿真、性能分析等)的使用方法。通过mcp-use库的MCPAgent和MCPClient组件,用户可以轻松实现自然语言驱动的浏览器操作和测试流程管理。

blues_C的博客 3800

小米.NET岗位解析与面试指南

随着.NET Core到.NET 5+的跨平台特性日益成熟,其在服务端开发中的竞争力显著提升,特别是在性能方面已经可以媲美甚至超越主流Java框架。这一技术演进使得越来越多的科技公司,如小米,开始重新评估.NET在其技术体系中的位置。小米的.NET岗位特别强调WPF和ASP.NET Core的应用,反映了其在PC端软件生态建设和云服务后端技术栈迁移的需求。对于开发者而言,掌握.NET Core/5+的深度知识、分布式系统开发经验以及云原生技术栈(如Docker和Kubernetes)将成为职业发展的关键。本

weixin_34054931的博客 380

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Suring开发集成部署时问题记录

前言 开发时一定要用管理员模式打开VS或者VSCODE进行开发,同时不要在nuget上直接下载,要去github上下载源代码调试。第一方便调试,第二Surging迭代较快,nuget版本往往不是最新的。 错误 问题:fail: Surging.Core.DotNetty.DotNettyServerMessageListener[0] 服务主机启动失败XXX 使用netstat -...

weixin_30449239的博客 317

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

ASP.NET Core笔记(2) - 依赖注入

服务的生命周期 链式注入时,生存期的选择 TryAdd与泛型注入 替换内置服务容器 ASP.NET Core提供了默认的依赖注入容器,可以在Startup.ConfigureServices方法中进行服务注入的配置。 服务的生命周期 默认的依赖注入容器提供了三种生命周期: 暂时(AddTransient),每次在向服务容器进行请求时都会创建新的实例,这种生存期适合轻量级、 无状态的服务。 范...

zhixin9001的博客 1万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

2019-7-13 Asp.net MVC 4期末试卷公布

2018—2019学年第2学期《Asp.Net MVC 》试卷-A 一.单选题(每题1分,共16分) 重要提示:个别题目多选,见注解.选择的字母填写到下方表格中,在其它地方填写的字母无效 1. 2. 3. 4. 5. 6. 7. ...

格码老师教编程 1339

ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

在 ASP.NET 中,我们知道,它有一个面向切面的请求管道,有19个主要的事件构成,能够让我们进行灵活的扩展。通常是在 web.config 中通过注册 HttpModule 来实现对请求管道事件监听,并通过 HttpHandler 进入到我们的应用程序中。而在 ASP.NET Core 中,对请求管道进行了重新设计,通过使用一种称为中间件的方式来进行管道的注册,同时也变得更加简洁和强大。 目录...

weixin_33696106的博客 389

No suitable constructor found for type

No suitable constructor found for type 异常描述 [2018-09-02T11:52:35.038] ERROR [020-exec-5] c.t.util.ShopUtils[111] 反序列化序列化attributes,从Json到List报错 org.codehaus.jackson.map.JsonMappingException: N...

走在命运的左岸 7865

default constructor not found 异常解决方法

Exception in thread "Thread-13" com.alibaba.fastjson.JSONException: default constructor not found. class com.nowcoder.async.EventModel at com.alibaba.fastjson.util.JavaBeanInfo.build(JavaBeanInfo.j

JavaEdge全是干货的技术号 4万+

ASP.NET Core基础

1、什么是ASP Net Core? 首先ASP net core不是 asp net的升级版本。它遵循了dot net的标准架构, 可以运行于多个操作系统上。它更快,更容易配置,更加模块化,可扩展性更强。 2、ASP Net Core有哪些好的功能? 第一是依赖注入。 第二是日志系统架构。 第三是引入了一个跨平台的网络服务器,kestrel。可以没有iis, apache和nginx就可以单独运行。 第四是可以使用命令行创建应用。 第五是使用APP settings json file.

weixin_45875616的博客 737

RESTFUL提示java.lang.NoSuchMethodException: Could not find a suitable constructor in

出现这个问题是因为我们写了一个接口,全部的restful的annotation都写在了这个接口上,然后让提供服务的类实现这个接口。就导致了这个问题。 其实想一想还是很容易理解的,我们可能写多个类实现这个接口,这样就会出现找不到到底哪个子类才是我们想要的实现的情况。 请看stackoverflow上面的一篇讨论:http://stackoverflow.com/questions

Mr_Pang的专栏 4185

Jackson异常问题和解决方案总结

转自:http://www.baeldung.com/jackson-exception 1. 前言 在本教程中, 我们会回顾最常见的Jackson异常 – the JsonMappingException and UnrecognizedPropertyException. 最后,我们将会简要讨论Jackson 中的no such method错误。 2. “Json

蚁人的博客 6万+

asp.net core AddTransient、AddScoped和AddSingleton三者的区别

一直对三个注入方式有点混淆在此记录 AddTransient 每次请求都是获得一个新的实例 AddScoped 对于同一个请求返回同一个实例,不同的请求返回不同的实例 AddSingleton 每次都是获得同一个实例 ...

narojay的博客 3791

AddTransient、AddSingleton、AddScoped 三者都应该在什么场景下使用

     网上随便一搜,能搜出一大堆对三者进行区别分析的文章,但是呢,理论是一回事,实际使用又是另外一回事,到底在何种场景下应该使用何种注入方式呢?      通过这篇文章和我自身的实际经验,来说一说实际应用中的情况:   首先 AddTransient,这个文章中说的挺明白,就是当组件无法共享时,将使用Transient。非线程安全的数据库访问对象就是一个例子。   根据我实...

weixin_30868855的博客 1991

谈谈中间件:AddSingleton AddScoped AddTransient 三者的差异

简介 AddSingleton:此方法创建一个 Singletion 服务。首次请求时创建 Singleton 服务。然后,所有后续的请求中都会使用相同的实例。因此,通常每个应用程序只创建一次 Singleton 服务,并且在整个应用程序生命周期中使用该单个实例 AddScoped:此方法创建一个 Scoped 服务。在范围内的每个请求中创建一个新的 Scoped 服务实例。例如在 We...

覃鸿宇的博客 6665
上一篇: 你不得不知道的 .NET CORE—— Startup 的秘密
下一篇: 在ASP.NET CORE中使用AUTOMAPPER
.NET跨平台
博客等级 码龄15年 6066粉丝 738原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值