vue视频教程 百度搜索优化 android教程 map遍历 vbscript ros 打印 binding testng Way.js vue架构 郑州普通话考试 jq遍历 mysql在线测试 linux超级用户 mysql数据库名称 xcode打包 磁盘清理会误删东西吗 linuxmysql启动命令 python定义一个变量 java入门编程 java继承关键字 randomjava java运算 jdbc连接mysql java终止线程 java中class java获取当前线程 java获取本机ip java中map 蓝牙运动耳机排行榜 js添加元素 两表关联查询 flash制作工具 博途v14安装教程 pycharm中文版 php递归 黑客入门新手特训 深入解析windows操作系统 fireworks 马哥python
当前位置: 首页 > 学习教程  > 编程语言

ASP.NET Core 中内置的依赖注入框架

2020/12/5 10:04:51 文章标签:

目录前言:一、使用容器注入和创建对象二、基类或接口有多次注册三、根容器和子容器创建实例四、容器创建的实例释放的策略五、多个构造函数时容器的选择策略参考文档前言: ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,这是一种在类及其…

目录

    • 前言:
    • 一、使用容器注入和创建对象
    • 二、基类或接口有多次注册
    • 三、根容器和子容器创建实例
    • 四、容器创建的实例释放的策略
    • 五、多个构造函数时容器的选择策略

    • 参考文档

前言:

ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,这是一种在类及其依赖关系之间实现控制反转 (IoC) 的技术。
ASP.NET Core 内置的依赖注入框架需要引入NuGet包Microsoft.Extensions.DependencyInjection
该包依赖于Microsoft.Extensions.DependencyInjection.Abstractions
后者是抽象,前者是具体实现,这就是 接口实现分离模式

一、使用容器注入和创建对象

首先准备三个类

    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 内置的依赖注入框架 有个根容器和子容器的概念,
子容器只关心根容器,子容器和子容器创建的子容器是平级的。
单例模式 Singleton 保存在根容器
作用域模式 Scoped 保存在当前容器
瞬时模式 Transient 不保存,一次性的

首先准备三个类并继承同一个基类,基类构造函数中打印对象信息。

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

上面的代码得到的结果为如下,
可以得到结论,
容器会提供构造函数所需要的所有参数,如果某个构造函数的参数类型集合,是所有合法构造函数参数类型集合的超集,那么这个构造函数就会被容器选择。
如果不存在某个构造函数的参数类型集合是所有合法构造函数参数类型集合的超集,并且有至少两个构造函数参数类型集合之间互存在差集,那么程序将会出现异常。

> Ctor(ILogger, IHttp)


参考文档

dotnet / runtime - Microsoft.Extensions.DependencyInjection


本文链接: http://www.dtmao.cc/news_show_450131.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?