.NET Core 依赖注入 Microsoft.Extensions.DependencyInjection

2023-12-11 22:21

本文主要是介绍.NET Core 依赖注入 Microsoft.Extensions.DependencyInjection,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • 什么是依赖注入
  • C# 使用依赖注入
    • 框架介绍
  • Microsoft.Extensions.DependencyInjection
    • Nuget安装
    • 简单单例使用
      • 打印结果
    • 自动装配
      • 举例
      • 自动装配测试用例
      • 打印结果
      • 自动装配执行顺序
        • 测试用例
        • 有歧义构造函数
        • 渐进式构造函数
        • 循环依赖
      • 自动装配结论
    • 手动装配
      • 手动注入
      • 别名注入
    • 依赖注入的构造顺序
  • 结尾

前言

依赖注入是一个非常重要的编程思想,就和面向过程和面向对象一样,IOC和控制反转是一种解耦的编程思想。

什么是依赖注入

[C#]理解和入门依赖注入

为什么要用IOC:inversion of controll反转控制(把创建对象的权利交给框架)

C# 使用依赖注入

框架介绍

目前.NET 有两个最优的依赖注入框架

  • Microsoft.Extensions.DependencyInjection:微软官方依赖注入框架,听说在.net core 8.0得到了最强的性能提升
  • Autofac:听说也是最强的依赖注入框架,性能强,开销低,功能完善。

Dependency injection in ASP.NET Core

Autofac 官网

深入浅出依赖注入容器——Autofac

Microsoft.Extensions.DependencyInjection

目前打算用微软的IOC,毕竟是官方背书,性能有保证。

C# 依赖注入IServiceCollection的AddSingleton方法使用

Nuget安装

在这里插入图片描述

简单单例使用

声明个测试类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace NETCore8.Models
{public class Person{public int Id { get; set; }public string ?Name { get; set; }public int Age { get; set; }}
}

主函数代码

using Microsoft.Extensions.DependencyInjection;
using NETCore8.Models;
using Newtonsoft.Json;
using System.ComponentModel.Design;namespace NETCore8
{internal class Program{static void Main(string[] args){//构造依赖注入容器IServiceCollection services = new ServiceCollection();//注入Person单例,生命周期暂时不展开services.AddSingleton<Person>();var builder = services.BuildServiceProvider();//初始化单例var res = builder.GetService<Person>();res.Name = "小刘";res.Age = 15;Console.WriteLine(JsonConvert.SerializeObject(res));//从容器中拿到Person单例,确认是否已被赋值为小刘var res2 = builder.GetService<Person>();Console.WriteLine(JsonConvert.SerializeObject(res2));//修改单例,查看容器中的单例是否被修改res2.Name = "小红";res2.Age = 23;//再从容器中拿出单例var res3 = builder.GetService<Person>();Console.WriteLine(JsonConvert.SerializeObject(res3));Console.WriteLine("Hello, World!");Console.ReadKey();}}
}

打印结果

在这里插入图片描述
这个说明这个单例一旦被修改了,容器中的数据就会被修改。但是这样仅仅是和全局静态的效果一样。依赖注入没有这么简单

自动装配

自动装配的意思就是自动依赖注入。就是你不需要主动去声明构造函数,IOC容器会自动帮你去使用构造函数。

举例

这里为了简单说明,这里只使用单例自动装配举例。

namespace IOC_Test.Models
{public class Person{public int Id { get; set; }public string Name { get; set; }public int Age {  get; set; }   }
}
namespace IOC_Test.Services
{public class PersonService{public Person Person { get; set; }/// <summary>/// 无参构造函数/// </summary>public PersonService() {Person = new Person();}/// <summary>/// 有参构造函数,IOC是选择尽可能多的参数构造/// </summary>/// <param name="person"></param>public PersonService(Person person){this.Person = person;}}
}

自动装配测试用例

using IOC_Test.Models;
using IOC_Test.Services;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;namespace IOC_Test
{internal class Program{static void Main(string[] args){IServiceCollection services = new ServiceCollection();//注入依赖services.AddSingleton<Person>();services.AddSingleton<PersonService>();//生成IOC容器var builder = services.BuildServiceProvider();//两次打印,第一次打印PersonService的Person{var res = builder.GetService<PersonService>();Console.WriteLine(JsonConvert.SerializeObject(res?.Person));}//修改Person,看看PersonService里面是不是会受影响{var res = builder.GetService<Person>();res.Name = "小王";res.Age = 10;}//再次打印,如果被修改,那么就说明是自动装配。如果没被修改,就说明没有将Person自动注入到PersonService{var res = builder.GetService<PersonService>();Console.WriteLine(JsonConvert.SerializeObject(res?.Person));}Console.WriteLine("Hello, World!");Console.ReadLine();}}
}

打印结果

在这里插入图片描述

自动装配执行顺序

测试用例

这里我们新建一个Dog

namespace IOC_Test.Models
{public class Dog{public int Id { get; set; }public string Name { get; set; }public int Age { get; set; }}
}

Person

namespace IOC_Test.Models
{public class Person{public int Id { get; set; }public string Name { get; set; }public int Age {  get; set; }   }
}

PersonService

namespace IOC_Test.Services
{public class PersonService{public Person Person { get; set; }public Dog Dog { get; set; }/// <summary>/// 无参构造函数/// </summary>public PersonService() {Person = new Person();}}
}

主函数

namespace IOC_Test
{internal class Program{static void Main(string[] args){IServiceCollection services = new ServiceCollection();//注入依赖services.AddSingleton<Person>();services.AddSingleton<PersonService>();services.AddSingleton<Dog>();//生成IOC容器var builder = services.BuildServiceProvider();//两次打印,第一次打印PersonService{var res = builder.GetService<PersonService>();Console.WriteLine(JsonConvert.SerializeObject(res));}//修改Person和Dog,看看PersonService里面是不是会受影响{var person = builder.GetService<Person>();person.Name = "小王";person.Age = 10;var dog = builder.GetService<Dog>();dog.Name = "旺财";dog.Age = 2;}//再次打印,查看自动装配如何执行{var res = builder.GetService<PersonService>();Console.WriteLine(JsonConvert.SerializeObject(res));}Console.WriteLine("Hello, World!");Console.ReadLine();}}
}
有歧义构造函数
namespace IOC_Test.Services
{public class PersonService{public Person Person { get; set; }public Dog Dog { get; set; }/// <summary>/// 无参构造函数/// </summary>public PersonService() {Person = new Person();}public PersonService(Person person){this.Person = person;}public PersonService(Dog dog) {this.Dog = dog;}}
}

如果构造函数出现歧义,比如这里既可以选择Person构造,又可以选择Dog构造,会报错
在这里插入图片描述

渐进式构造函数
namespace IOC_Test.Services
{public class PersonService{public Person Person { get; set; }public Dog Dog { get; set; }/// <summary>/// 无参构造函数/// </summary>public PersonService() {Person = new Person();}public PersonService(Person person){this.Person = person;}public PersonService(Person person,Dog dog) {this.Person= person;this.Dog = dog;}}
}

运行成功
在这里插入图片描述

循环依赖

Person注入Dog,Dog注入Person,看看效果如何

namespace IOC_Test.Models
{public class Person{public Dog Dog { get; set; }public int Id { get; set; }public string Name { get; set; }public int Age { get; set; }public Person(Dog dog){Dog = dog;}}
}
namespace IOC_Test.Models
{public class Dog{public int Id { get; set; }public string Name { get; set; }public int Age { get; set; }public Person Person { get; set; }public Dog(Person person){Person = person;}}
}

在这里插入图片描述

自动装配结论

自动装配是尽可能主动去装配服务,如果出现装配歧义,循环依赖,那么就会主动抛出异常。自动装配可以极大的减少对构造函数维护,我们不需要知道服务是怎么声明的,IOC容器会帮助我们自动声明相互之间的依赖。这张图就能很好的解释自动装配的效果

在这里插入图片描述

手动装配

自动装配是由IOC容器自动装配的类。如果需要装配多个同类的服务,那就要手动进行区别了。

手动注入

internal class Program
{static void Main(string[] args){IServiceCollection services = new ServiceCollection();services.AddSingleton<Person>(sp =>{var res = new Person() {Name = "小红",Age = 19};return res;});//生成容器var builder = services.BuildServiceProvider();{var res = builder.GetService<Person>();Console.WriteLine(JsonConvert.SerializeObject(res));}Console.WriteLine("Hello, World!");Console.ReadLine();}
}

在这里插入图片描述

别名注入

namespace IOC_Test
{internal class Program{static void Main(string[] args){IServiceCollection services = new ServiceCollection();services.AddKeyedSingleton<Person>("A",(sp,key) =>{var res = new Person() {Name = "小红",Age = 19};return res;});services.AddKeyedSingleton<Person>("B", (sp, key) =>{var res = new Person(){Name = "小蓝",Age = 23};return res;});//生成容器var builder = services.BuildServiceProvider();//获取服务,当Key找不到时自动返回Null{var res = builder.GetService<Person>();Console.WriteLine("获取默认服务");Console.WriteLine(JsonConvert.SerializeObject(res));}{var res = builder.GetKeyedService<Person>("A");Console.WriteLine("获取A,Person");Console.WriteLine(JsonConvert.SerializeObject(res));}{var res = builder.GetKeyedService<Person>("B");Console.WriteLine("获取B,Person");Console.WriteLine(JsonConvert.SerializeObject(res));}Console.WriteLine("Hello, World!");Console.ReadLine();}}
}

在这里插入图片描述

声明别名的服务将不会自动装配,即使声明的别名相同。建议使用多个不同名的服务来自动装配。手动声明别名需要手动装配对应关系

也可以在输入的时候主动拿到按照Key去寻找服务。

internal class Program
{static void Main(string[] args){IServiceCollection services = new ServiceCollection();//依赖注入是使用的时候去构造,所以声明顺序不影响实际运行顺序,有点类似于回调函数services.AddKeyedSingleton<Person>("A",(sp,key) =>{//Console.WriteLine(key);var res = new Person() {Name = "小红",Age = 19};return res;});services.AddKeyedSingleton<PersonService>("A", (sp, key) =>{return new PersonService(sp.GetKeyedService<Person>(key));});//生成容器var builder = services.BuildServiceProvider();//获取服务{var res = builder.GetKeyedService<Person>("A");Console.WriteLine("获取默认服务");Console.WriteLine(JsonConvert.SerializeObject(res));}//获取服务{var res = builder.GetKeyedService<PersonService>("A");Console.WriteLine("获取默认服务");Console.WriteLine(JsonConvert.SerializeObject(res));}Console.WriteLine("Hello, World!");Console.ReadLine();}
}

依赖注入的构造顺序

依赖注入是使用的时候去生成,而不是注入的时候生成

namespace IOC_Test
{internal class Program{static void Main(string[] args){IServiceCollection services = new ServiceCollection();services.AddKeyedSingleton<Person>("A",(sp,key) =>{Console.WriteLine($"构造函数执行,key[{key}]");var res = new Person() {Name = "小红",Age = 19};return res;});//生成容器var builder = services.BuildServiceProvider();//获取服务{Console.WriteLine("获取Key[A]服务");var res = builder.GetKeyedService<Person>("A");Console.WriteLine(JsonConvert.SerializeObject(res));}Console.WriteLine("Hello, World!");Console.ReadLine();}}
}

在这里插入图片描述

结尾

IOC容器还有许多别的功能,比如别名,接口注入,注解注入,声明周期等。这个我还不太了解。现在的单例自动装配已经基本满足了我的功能,我以后有时间会去深入了解。

这篇关于.NET Core 依赖注入 Microsoft.Extensions.DependencyInjection的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/482313

相关文章

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

IDEA Maven提示:未解析的依赖项的问题及解决

《IDEAMaven提示:未解析的依赖项的问题及解决》:本文主要介绍IDEAMaven提示:未解析的依赖项的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录IDEA Maven提示:未解析的依编程赖项例如总结IDEA Maven提示:未解析的依赖项例如

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

Java -jar命令如何运行外部依赖JAR包

《Java-jar命令如何运行外部依赖JAR包》在Java应用部署中,java-jar命令是启动可执行JAR包的标准方式,但当应用需要依赖外部JAR文件时,直接使用java-jar会面临类加载困... 目录引言:外部依赖JAR的必要性一、问题本质:类加载机制的限制1. Java -jar的默认行为2. 类加

java -jar命令运行 jar包时运行外部依赖jar包的场景分析

《java-jar命令运行jar包时运行外部依赖jar包的场景分析》:本文主要介绍java-jar命令运行jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作... 目录Java -jar命令运行 jar包时如何运行外部依赖jar包场景:解决:方法一、启动参数添加: -Xb

无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案

《无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案》:本文主要介绍了无法启动此程序,详细内容请阅读本文,希望能对你有所帮助... 在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是"api-ms-win-core-path-l1-1-0.dll丢失

Maven 依赖发布与仓库治理的过程解析

《Maven依赖发布与仓库治理的过程解析》:本文主要介绍Maven依赖发布与仓库治理的过程解析,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下... 目录Maven 依赖发布与仓库治理引言第一章:distributionManagement配置的工程化实践1

使用easy connect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题

《使用easyconnect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题》:本文主要介绍使用easyconnect之后,maven无法... 目录使用easGWowCy connect之后,maven无法使用,原来需要配置-DJava.net.pr

Spring三级缓存解决循环依赖的解析过程

《Spring三级缓存解决循环依赖的解析过程》:本文主要介绍Spring三级缓存解决循环依赖的解析过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、循环依赖场景二、三级缓存定义三、解决流程(以ServiceA和ServiceB为例)四、关键机制详解五、设计约