Article directory
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
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.
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
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
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
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.
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
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;
}
}
}
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.
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();
}
}
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();
}
}
}
声明别名的服务将不会自动装配,即使声明的别名相同。建议使用多个不同名的服务来自动装配。手动声明别名需要手动装配对应关系
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();
}
}
}
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.