.NET Core Dependency Injection Microsoft.Extensions.DependencyInjection

Preface

Dependency injection is a very important programming idea. Just like process-oriented and object-oriented, IOC and inversion of control are decoupled programming ideas.

What is dependency injection

[C#] Understanding and getting started with dependency injection

Why use IOC:inversion of control to invert control (give the right to create objects to the framework)

C# uses dependency injection

Framework introduction

Currently, .NET has two optimal dependency injection frameworks

  • Microsoft.Extensions.DependencyInjection: Microsoft’s official dependency injection framework. I heard that it has received the strongest performance improvement in .net core 8.0.
  • Autofac: I heard it is also the most powerful dependency injection framework, with strong performance, low overhead and complete functions.

Dependency injection in ASP.NET Core

Autofac official website

An in-depth introduction to dependency injection containers - Autofac

Microsoft.Extensions.DependencyInjection

Currently, I plan to use Microsoft's IOC. After all, it is officially endorsed and its performance is guaranteed.

C# dependency injection IServiceCollection's AddSingleton method is used

Nuget installation

Insert image description here

Simple singleton use

Declare a test class

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; }


    }
}

Main function code

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();
        }
    }
}


Print results

Insert image description here
This means that once the singleton is modified, the data in the container will be modified. But this only has the same effect as global static. Dependency injection is not that simple

automatic assembly

Autowiring means automatic dependency injection. That is, you don't need to actively declare the constructor, the IOC container will automatically use the constructor for you.

Example

For the sake of simple explanation, only singleton automatic assembly is used as an example.

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;
        }
    }
}

Automatic assembly of test cases

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();
        }
    }
}

Print results

Insert image description here

Automatic assembly execution sequence

test case

Here we create a new 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();
        }
    }
}

main function

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();
        }
    }
}

Ambiguous constructor
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;
        }
    }
}

If there is ambiguity in the constructor, for example, you can choose either Person construction or Dog construction, an error will be reported.
Insert image description here

Progressive constructor
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;
        }
    }
}

Run successfully
Insert image description here

circular dependency

Person is injected into Dog, Dog is injected into Person, and see what the effect is.

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;
        }
    }
}

Insert image description here

Automatic assembly conclusion

Automatic assembly is to proactively assemble services as much as possible. If there are assembly ambiguities or circular dependencies, exceptions will be actively thrown. Automatic assembly can greatly reduce constructor maintenance. We do not need to know how the service is declared. The IOC container will help us automatically declare mutual dependencies. This picture can well explain the effect of automatic assembly.

Insert image description here

Manual assembly

Autowiring is a class that is automatically assembled by an IOC container. If you need to assemble multiple services of the same type, you need to distinguish them manually.

Manual injection

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();
    }
}

Insert image description here

Alias ​​injection



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();
        }
    }
}

Insert image description here

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

You can also actively get the key to search for services when typing.

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();
    }
}

Dependency injection construction order

Dependency injection is generated when used, not when injected.

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();
        }
    }
}

Insert image description here

end

The IOC container also has many other functions, such as aliases, interface injection, annotation injection, declaration cycle, etc. I don't know much about this yet. The current singleton automatic assembly has basically satisfied my functions. I will learn more about it when I have time in the future.

Guess you like

Origin blog.csdn.net/qq_44695769/article/details/134819501