栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

.Net 中使用依赖注入(DI)(一)

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

.Net 中使用依赖注入(DI)(一)

.Net 中使用 DI

依赖注入(Dependency Injection,DI)是控制反转(Inversion of Control,IOC)思想的实现方式。

依赖注入简化模块的组装过程,降低模块之间的耦合度

为什么使用DI?

假设有这样的接口和实现类:

public interface ISayHello
{
    public void SayHello();
}

public class Chinese : ISayHello, IDisposable
{
    public void SayHello()
    {
        Console.WriteLine("你好!");
    }

    public void Dispose()
    {
        Console.WriteLine("资源释放了");
    }
}

调用方式:

ISayHello sayHello = new Chinese();
sayHello.SayHello();

基于接口编程自有其好处,但是如上面的方式还是有缺陷的。调用者必须知道是谁实现了ISayHello接口的SayHello方法。这里就需要知道一个接口,一个实现类,加上本身调用的地方,一个简单的调用就有三者耦合。

DI 原理

下面让我们通过 DI 的方式实现,首先是 nuget 安装:

Install-Package Microsoft.Extensions.DependencyInjection -Version 6.0.0

上面包是微软实现的实现了依赖注入的包,看看怎么使用(最麻烦的使用方式):

ServiceCollection serviceContainer = new ServiceCollection();  // 构建一个服务容器
serviceContainer.AddTransient();  // 把 Chinese 作为瞬态服务(对象),添加到服务器容器

// 通过容器构建一个 ServiceProvider(服务提供器),ServiceProvider 可以提供容器中管理的服务(已添加到容器的服务)
using (ServiceProvider serviceProvider = serviceContainer.BuildServiceProvider())  
{
    ISayHello sayHello = serviceProvider.GetService();  // 从 ServiceProvider 中获取需要的服务(对象)
    sayHello.SayHello();
}
  1. 首先创建一个服务(对象)容器
  2. 把需要服务容器管理的服务(对象)添加到服务容器
  3. 通过服务容器构建一个 ServiceProvider(服务提供器),ServiceProvider 可以提供容器管理的服务(已添加到容器的服务)
  4. 通过 ServiceProvider(服务提供器)获取需要的服务(对象)
DI 的生命周期 Transient(瞬态)

使用一次即销毁,下次使用将是一个新的对象

Singleton(单例)

顾名思义,参考单例模式

Scoped(范围)

在一个范围内使用同一个对象

可以通过ServiceProvider的CreateScope方法创建新的范围:

ServiceCollection serviceContainer = new ServiceCollection();
serviceContainer.AddScoped();

using (ServiceProvider serviceProvider = serviceContainer.BuildServiceProvider())
{
    ISayHello sayHello1 = serviceProvider.GetService();
    ISayHello sayHello2 = serviceProvider.GetService();
    ISayHello sayHello3 = null;  // 应该在 Scope 范围内声明变量
    ISayHello sayHello4 = null;  // 应该在 Scope 范围内声明变量
    ISayHello sayHello5 = null;  // 应该在 Scope 范围内声明变量
    ISayHello sayHello6 = null;  // 应该在 Scope 范围内声明变量

    using (IServiceScope serviceScope = serviceProvider.CreateScope())
    {
        sayHello3 = serviceScope.ServiceProvider.GetService();
        sayHello4 = serviceScope.ServiceProvider.GetService();
    }

    using (IServiceScope serviceScope = serviceProvider.CreateScope())
    {
        sayHello5 = serviceScope.ServiceProvider.GetService();
        sayHello6 = serviceScope.ServiceProvider.GetService();
    }

    Console.WriteLine(Object.ReferenceEquals(sayHello1, sayHello2));  // True,  都是通过 serviceProvider 创建
    Console.WriteLine(Object.ReferenceEquals(sayHello3, sayHello4));  // True, 都是通过同一个 serviceScope 创建,在单独的一个范围中
    Console.WriteLine(Object.ReferenceEquals(sayHello1, sayHello4));  // False,在单独的一个范围中,所以和 serviceProvider 创建的不一致
    Console.WriteLine(Object.ReferenceEquals(sayHello5, sayHello6));  // True, 同 sayHello3, sayHello4
    Console.WriteLine(Object.ReferenceEquals(sayHello1, sayHello6));  // False,同 sayHello1, sayHello4
    Console.WriteLine(Object.ReferenceEquals(sayHello4, sayHello6));  // False,在两个 serviceScope 中,所以 False
}

为了确保对象正确的生命周期,应该在 Scope 范围内声明变量

由上图可以知道,如果实现类实现了 IDisposable接口,则在离开作用域之后,容器会自动调用对象的Dispose方法。

也印证了需要在范围内声明并使用对象,在外面可能资源被释放了

如何选择生命周期?

如果一个类是无状态的,这时可以选择 Singleton。因为无状态的类无所谓并发问题(不会修改状态)。
如果一个类有状态,并且有 Scope 控制,可以注册为 Scoped。因为 Scope 控制的代码都是运行在同一个线程中的,没有并发修改的问题。
最后 Transient 在使用时,需要谨慎,因为每次都会创建一个新的对象。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/910799.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号