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

前言

依赖注入是一个非常重要的编程思想,就和面向过程和面向对象一样,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容器还有许多别的功能,比如别名,接口注入,注解注入,声明周期等。这个我还不太了解。现在的单例自动装配已经基本满足了我的功能,我以后有时间会去深入了解。

猜你喜欢

转载自blog.csdn.net/qq_44695769/article/details/134819501
今日推荐