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

news2024/10/5 20:19:04

文章目录

  • 前言
  • 什么是依赖注入
  • 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容器还有许多别的功能,比如别名,接口注入,注解注入,声明周期等。这个我还不太了解。现在的单例自动装配已经基本满足了我的功能,我以后有时间会去深入了解。

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

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

相关文章

ShardingSphere数据分片之分表操作

1、概述 Apache ShardingSphere 是一款分布式的数据库生态系统&#xff0c; 可以将任意数据库转换为分布式数据库&#xff0c;并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。 Apache ShardingSphere 设计哲学为 Database Plus&#xff0c;旨在构建异构数据库上…

L1-030:一帮一

题目描述 “一帮一学习小组”是中小学中常见的学习组织方式&#xff0c;老师把学习成绩靠前的学生跟学习成绩靠后的学生排在一组。本题就请你编写程序帮助老师自动完成这个分配工作&#xff0c;即在得到全班学生的排名后&#xff0c;在当前尚未分组的学生中&#xff0c;将名次最…

使用Postman进行自动化集成测试

1 前言 笔者在使用Node开发HTTP接口的过程中&#xff0c;发现当接口数量越来越多&#xff0c;且接口之间互相依赖时&#xff0c;接口测试流程就会变得十分繁琐&#xff0c;且容易出错。那如何才能高效且全面地对接口进行测试呢&#xff1f; 通过实践&#xff0c;笔者发现可以…

程序员的职业连续性就那么重要吗?

大家好&#xff0c;我是风筝&#xff0c;微信搜「古时的风筝」&#xff0c;更多干货 年初的时候时候一个同学跟我聊天说起最近面试的经历。说投了不少简历&#xff0c;但是面试的机会不多&#xff0c;而且有的负责照片的 HR 直接跟他说&#xff1a;“你的工作连续性不达标&…

AI专题报告:AI多模态提升商业化价值,应用多点开花验证景气度

今天分享的AI系列深度研究报告&#xff1a;《AI专题报告&#xff1a;AI多模态提升商业化价值&#xff0c;应用多点开花验证景气度》。 &#xff08;报告出品方&#xff1a;太平洋证券&#xff09; 报告共计&#xff1a;21页 1 一周行情回顾 上周上证综指、深证成指、创业板指…

两个月软考-高项上岸

文章目录 前言结缘软考功亏一篑有始有终2个月计划资料部分计划截图 总结 前言 我们看小说或者电视剧电影都会看到这样的情节&#xff0c;主角一开始锦衣玉食&#xff0c;突然家道中落&#xff0c;啥都没了&#xff0c;主角再一路奋起重新找回了属于自己的一切&#xff1b;还有…

视频汇聚/音视频流媒体视频平台/视频监控EasyCVR分享页面无法播放,该如何解决?

国标GB28181安防视频监控/视频集中存储/云存储EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统…

数据库系统概论期末经典大题讲解(范式提升、求闭包、求主码)

上一次我们介绍了数据库中关系代数查询&#xff0c;从选择、投影到连接等操作符&#xff0c;探索了数据库查询 大家可以移步我的文章&#xff1a;数据库系统概论期末经典大题讲解&#xff08;用关系代数进行查询&#xff09;-CSDN博客 今天&#xff0c;我们将继续沿着数据库系统…

111.am40刷机折腾记4-firefly镜像-dp正常显示

1. 平台&#xff1a; rk3399 am40 4g32g 2. 内核&#xff1a;firefly的内核&#xff08;整体镜像&#xff09; 版本&#xff1a; linux4.4.194 3. 交叉编译工具 &#xff1a;暂时不编译 4. 宿主机&#xff1a;ubuntu18.04 5. 需要的素材和资料&#xff1a;boot-am40-202…

14、SQL注入——HTTP文件头注入

文章目录 一、HTTP Header概述1.1 HTTP工作原理1.2 HTTP报文类型1.3 较重要的HTTP Header内容 二、HTTP Header注入2.1 HTTP Header注入的前提条件2.2 常见的HTTP Header注入类型 一、HTTP Header概述 1.1 HTTP工作原理 1.2 HTTP报文类型 &#xff08;1&#xff09;请求报文 …

CrystalDiskInfo中文版(硬盘检测工具) v9.1.1.0 绿色汉化版-供大家学习研究参考

更新内容 重新支持三星SATA SSD寿命报告 增加对ZHITAI SC001的支持 新增SK hynix Gold S31支持 增加了KLEVV NEO N610的支持。 改进的Micron/Crucial SATA SSD支持 已更改 卸载程序将显示一个确认对话框&#xff0c;用于删除设置。 强大功能 1.拥有多国语言&#xff0c;…

element中el-select多选v-model是对象数组

文章目录 一、问题二、解决三、最后 一、问题 element中的el-select的v-model一般都是字符串或者字符串数组&#xff0c;但是有些时候后端接口要求该字段要传对象或者对象数组&#xff0c;如果再转换一次数据&#xff0c;对于保存配置和回显都是吃力不讨好的事情。如下所示&am…

Linux--学习记录(2)

解压命令&#xff1a; gzip命令&#xff1a; 参数&#xff1a; -k&#xff1a;待压缩的文件会保留下来&#xff0c;生成一个新的压缩文件-d&#xff1a;解压压缩文件语法&#xff1a; gzip -k pathname(待压缩的文件夹名)gzip -kd name.gz&#xff08;待解压的压缩包名&#x…

数据库版本管理框架-Flyway(从入门到精通)

一、flyway简介 Flyway是一个简单开源数据库版本控制器&#xff08;约定大于配置&#xff09;&#xff0c;主要提供migrate、clean、info、validate、baseline、repair等命令。它支持SQL&#xff08;PL/SQL、T-SQL&#xff09;方式和Java方式&#xff0c;支持命令行客户端等&am…

SpringIOC之@Configuration

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

手机的隐私空间的相关知识

一、删除: 进入隐私空间: 打开手机的设置,找到隐私空间(或类似名称)选项,然后输入密码或验证方式进入隐私空间。浏览内容: 在隐私空间中,您可以浏览存储在其中的照片、视频、文件等内容。选择删除: 选择您想要删除的内容,通常可以通过长按或选中复选框的方式进行多选…

孩子都能学会的FPGA:第二十六课——用FPGA实现CIC抽取滤波器

&#xff08;原创声明&#xff1a;该文是作者的原创&#xff0c;面向对象是FPGA入门者&#xff0c;后续会有进阶的高级教程。宗旨是让每个想做FPGA的人轻松入门&#xff0c;作者不光让大家知其然&#xff0c;还要让大家知其所以然&#xff01;每个工程作者都搭建了全自动化的仿…

第二次上机测试:Javascript. (第一组)

需求&#xff1a;&#xff08;注意在规定的时间&#xff0c;在自己的博客提交作品&#xff0c;便于统计测试的完成情况。博客题目《第二次上机测试&#xff1a;Javascript.》&#xff09; 1、点击按钮&#xff0c;将图片加上边框 2、点击按钮&#xff0c;将图片缩小为长和宽都…

windows安装protoc、protoc-gen-go、protoc-gen-go-grpc

文章目录 一、 protoc二、protoc-gen-go三、protoc-gen-go-grpc 一、 protoc 1&#xff0c;下载&#xff1a;https://github.com/google/protobuf/releases 下载对应的protoc&#xff0c;注意选择windows 2&#xff0c;下好之后解压就行&#xff0c;然后把bin目录加入到环境…