本文章将介绍 ASP.NET Core 内置的依赖注入及其生命周期和策略,并使用 Code 帮助理解
先决条件
.NET 8 SDK
介绍
ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式
这是一种在类及其依赖关系之间实现控制反转 (IoC) 的技术
使用容器注入和创建对象
首先准备三个类供后面的容器创建对象
     public interface ILogger { }    public class Logger : ILogger { }
    public interface IServer { }    public class Server : IServer { }
    public interface IHttp { }    public class Http : IHttp { }
   复制代码
 
将三个类注册到不同的生命周期中,然后循环打印注册的类型信息
最后 Build 出 容器(服务提供对象),使用 容器 创建对象,并断言对象是否为预期的对象
     var collection = new ServiceCollection()        .AddTransient<ILogger, Logger>()        .AddScoped<IServer, Server>()        .AddSingleton<IHttp, Http>();        foreach (var item in collection)    {    	var log = $"Service: {item.ServiceType.FullName} Lifetime: {item.Lifetime}\nInstance: {item.ImplementationType?.FullName}\n";        Console.WriteLine(log);    }        var provider = collection.BuildServiceProvider();    Debug.Assert(provider.GetService<ILogger>() is Logger);    Debug.Assert(provider.GetService<IServer>() is Server);    Debug.Assert(provider.GetService<IHttp>() is Http);
   复制代码
 基类或接口被多次注册
首先准备三个类并继承同一个基类
     public class Base { }
    public interface ILogger { }    public class Logger : Base, ILogger { }
    public interface IServer { }    public class Server : Base, IServer { }
    public interface IHttp { }    public class Http : Base, IHttp { }
   复制代码
 
将三个类注册到容器之后,Build 出 容器(服务提供对象)
使用 容器 创建对象,并断言重复注册的基类得到的对象是最后一个
使用 容器 的 GetServices 方法可以获取所有注册类型的对象,并按照注册顺序排列
     var collection = new ServiceCollection()        .AddTransient<Base, Logger>()        .AddScoped<Base, Server>()        .AddScoped<Base, Http>();        var provider = collection.BuildServiceProvider();        Debug.Assert(provider.GetService<Base>() is Http);        var bases = provider.GetServices<Base>().ToList();    Debug.Assert(bases[0] is Logger);    Debug.Assert(bases[1] is Server);    Debug.Assert(bases[2] is Http);
   复制代码
 根容器和子容器创建实例
ASP.NET Core 内置的依赖注入框架 有个根容器和子容器的概念
首先准备三个类并继承同一个基类,基类构造函数中打印对象信息
     public class Base    {        public Base() => Console.WriteLine($"Created:{GetType().Name}");    }
    public interface ILogger { }    public class Logger : Base, ILogger { }
    public interface IServer { }    public class Server : Base, IServer { }
    public interface IHttp { }    public class Http : Base, IHttp { }
   复制代码
 
通过 ServiceCollection 对象 Build 的 容器 就是根容器,即这里的 root
在这里创建根容器以后,再通过根容器的 CreateScope 方法创建两个服务范围(作用域)
通过访问服务范围的 ServiceProvider 属性,可以拿到服务范围对应的子容器
最后我们通过不同的子容器连续两次创建不同的生命周期的实例
     var root = new ServiceCollection()        .AddTransient<ILogger, Logger>()        .AddScoped<IServer, Server>()        .AddSingleton<IHttp, Http>()        .BuildServiceProvider();        var child1 = root.CreateScope().ServiceProvider;    var child2 = root.CreateScope().ServiceProvider;        child1.GetService<ILogger>();    child1.GetService<ILogger>();    child1.GetService<IServer>();    child1.GetService<IServer>();    child1.GetService<IHttp>();    child1.GetService<IHttp>();    Console.WriteLine();    child2.GetService<ILogger>();    child2.GetService<ILogger>();    child2.GetService<IServer>();    child2.GetService<IServer>();    child2.GetService<IHttp>();    child2.GetService<IHttp>();
   复制代码
 
上面代码运行得到的结果为如下
根据结果我们可以得知
生命周期注册为 Transient 时,每次通过容器创建对象时都是全新的对象
生命周期注册为 Scoped 时,同一个子容器中只会创建一次
生命周期注册为 Singleton 时,不同的子容器只能创建一次并复用
 > Created:Logger> Created:Logger> Created:Server> Created:Http
> Created:Logger> Created:Logger> Created:Server
   复制代码
 容器创建的实例释放的策略
首先准备三个类并继承同一个基类,且基类实现 IDisposable 
     public class Base : IDisposable    {        public Base() => Console.WriteLine($"Created:{GetType().Name}");
        public void Dispose() => Console.WriteLine($"Disposed: {GetType().Name}");    }
    public interface ILogger { }    public class Logger : Base, ILogger { }
    public interface IServer { }    public class Server : Base, IServer { }
    public interface IHttp { }    public class Http : Base, IHttp { }
   复制代码
 
通过 using 释放容器,打印日志查看对象释放点
     var collection = new ServiceCollection()       .AddTransient<ILogger, Logger>()       .AddScoped<IServer, Server>()       .AddSingleton<IHttp, Http>();        using (ServiceProvider root = collection.BuildServiceProvider())    {        using (IServiceScope scope = root.CreateScope())        {            var child = scope.ServiceProvider;            var logger = child.GetService<ILogger>();            var server = child.GetService<IServer>();            var http = child.GetService<IHttp>();            Console.WriteLine("子容器释放");        }        Console.WriteLine("根容器释放");    }
   复制代码
 
上面代码运行得到的结果为如下
可以知道在容器释放对应生命周期的对象也会释放
 > Created:Logger> Created:Server> Created:Http> 子容器释放> Disposed: Server> Disposed: Logger> 根容器释放> Disposed: Http
   复制代码
 多个构造函数时容器的选择策略
首先准备四个类,其中 User 不会注册到容器中,Server 类中有三个构造函数
     public interface ILogger { }    public class Logger : ILogger { }
    public interface IHttp { }    public class Http : IHttp { }
    public interface IUser { }    public class User : IUser { }
    public interface IServer { }    public class Server : IServer    {        public Server(ILogger logger)            => Console.WriteLine($"Ctor(ILogger)");
        public Server(ILogger logger, IHttp http)            => Console.WriteLine($"Ctor(ILogger, IHttp)");
        public Server(ILogger logger, IHttp http, IUser user)            => Console.WriteLine($"Ctor(ILogger, IHttp, IUser)");    }
   复制代码
 
注册 Logger,Http,Server,不注册 User
Build 出容器以后,使用容器创建类型为 Server 的对象
     var root = new ServiceCollection()        .AddScoped<ILogger, Logger>()        .AddScoped<IHttp, Http>()        .AddScoped<IServer, Server>()        .BuildServiceProvider();        root.GetService<IServer>();
   复制代码
 
上面的代码得到的结果为如下
可以得到结论
参考文档
dotnet / runtime - Microsoft.Extensions.DependencyInjection
评论