banner
Chenh

Chenh

一文讲清依赖注入

一文讲清依赖注入#

先进行一个自我催眠,记住一点依赖注入是一种设计模式

看一个简单的例子:

// 1. 强依赖关系
public int MyStringTool(){
  string str = "Hello world!";
  return str.Count();
}
// 2. 无依赖关系
string str = "Hello world!";
public int MyStringTool(string str){
  return str.Count();
}

两个函数,一个是在函数内部创建局部变量,一个是通过参数传递变量。前者变量和函数具有强依赖关系(体现在局部变量只能在函数内部起作用)。后者变量通过参数传递给函数,函数具有了泛化处理能力,且变量与函数也没有依赖关系。

这就是依赖注入的基本思想。在类之间的衍生则是下面的例子:

// 1. 强依赖关系
public class MainObject{
  public Method(){
    var tool = new MyTool();
    // 使用tool
  }
}

// 2. 依赖注入
public class MainObject{
  public readonly MyTool _tool;
  MainObject(MyTool tool){
    _tool = tool;
  }
  public Method(){
    _tool.function();
    // 使用tool
  }
}

两种调用工具类 Tool 的方式,第一种在类内部实例化工具 MyTool,两者具有强依赖关系;第二种通过构造函数传递工具 MyTool 的实例,实现了依赖注入,即 依赖项 MyTool 通过构造函数 注入 进类 MainObject。

还有一个名词叫控制反转,我认为这个词更为贴切。

控制代表两者间的依赖关系,谁控制谁 / 谁依赖于谁(存在)

在第一个函数例子中,变量与函数的关系从 变量依赖于函数存在 / 函数控制着变量的生命周期 转换为 函数必须提供参数才能运行

在第二个类例子中,工具类与主体类的关系从 工具实例由主体类创建,并依赖与主体类存在 转换为 主体类的构造必须提供工具类 。这就体现了 反转 的含义。当然类具有默认构造函数,并不是非得提供工具类不可。

在 ASP.NET 中的依赖注入#

ASP.net 主体就是 WebApplication 类。它的工具类就是一系列 Services 服务下面是一个典型的 ASP.net 应用的入口程序(Program.cs)

// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var provider = builder.Services.BuildServiceProvider();
var app = builder.Build();
app.UseAuthorization();
app.MapControllers();

app.Run()

在这个样例程序里,Services就是通过依赖注入的方式为WebApplication提供服务的。

在本 Web 应用程序中,有一个独立的,为 App 提供功能支持的容器,这些容器之间,可以通过构造函数注入的方式相互获取实例。下面是依赖注入(DI)涉及到的关键的类和接口:

  1. IServiceCollection:这是用于注册服务的接口。您可以使用它的扩展方法来注册各种服务类型和实现。

  2. IServiceProvider:这是一个服务提供程序接口,用于解析和获取已注册的服务实例。它是由容器在需要时创建的。

  3. ServiceLifetime:这是一个枚举,用于指定注册服务的生命周期。常见的生命周期选项包括 Singleton(单例)、Scoped(作用域)和 Transient(瞬时)。

  4. AddTransientAddScopedAddSingleton:这些是 IServiceCollection 接口的扩展方法,用于将服务类型和其实现添加到服务容器中,并指定它们的生命周期。

  5. IServiceProvider.GetService<T>:这是 IServiceProvider 接口的方法,用于获取已注册的服务实例。您可以通过指定服务类型的泛型参数来获取特定类型的服务实例。

  6. 构造函数注入:这是一种常见的依赖关系注入方式,通过将依赖的服务作为参数传递给类的构造函数来实现。依赖项在创建类实例时自动解析和提供。

  7. 属性注入:除了构造函数注入外,您还可以使用属性注入来注入依赖项。在类中定义需要注入的属性,并使用相应的属性注入特性(如 [Inject][Autowired] 等)进行标记。

Tip. Services.AddXXX()就是为依赖容器中添加依赖项的方法,这些方法通过 C# 的扩展方法的语法特性提供。该语法可以在不改变类的情况下为其添加额外方法:

// 命名空间为 MyNamespace
public static int ExMethod(this String str){
  return str.Count();
}
// 使用
using MyNamespace
String str = new String("hello world!");
str.ExMethod();

在 .NET 中,服务是应用程序的依赖项,可以使用依赖注入(DI)进行管理。服务可以是在应用程序中定义的类、接口或其他组件,它们提供特定的功能或服务。

当一个类需要使用其他服务时,可以通过构造函数注入的方式直接引入这些依赖项。通过将依赖项作为构造函数参数声明,DI 容器会自动检测到这些依赖项,并在创建类的实例时自动提供它们。

示例:

public class MyService
{
    private readonly IAnotherService _anotherService;

    public MyService(IAnotherService anotherService)
    {
        _anotherService = anotherService;
    }

    public void DoSomething()
    {
        // 使用依赖项进行操作
        _anotherService.SomeMethod();
    }
}

在上面的示例中,MyService 类需要使用 IAnotherService 接口的实现。通过将 IAnotherService 作为构造函数参数声明,DI 容器会在创建 MyService 的实例时自动提供它。

对于外部(静态)类或方法,如果需要使用依赖项,可以通过 IServiceProvider 接口来获取它们。IServiceProvider 是一个服务提供程序接口,用于解析和获取已注册的服务实例。

示例:

public static class MyStaticClass
{
    public static void DoSomething(IServiceProvider serviceProvider)
    {
        // 通过IServiceProvider获取依赖项
        var anotherService = serviceProvider.GetService<IAnotherService>();
        
        // 使用依赖项进行操作
        anotherService.SomeMethod();
    }
}

在上面的示例中,MyStaticClass 是一个静态类,它的方法 DoSomething 需要使用 IAnotherService 的实例。通过将 IServiceProvider 作为参数传递给 DoSomething 方法,可以使用 GetService 方法从 IServiceProvider 中获取 IAnotherService 的实例。

总结来说,通过依赖注入和 IServiceProvider,我们可以在构造函数中直接引入依赖项,并让 DI 容器自动提供;对于外部(静态)类或方法,可以通过 IServiceProvider 获取依赖项,实现依赖项的使用和解耦。

依赖项生命周期#

当我们把Services用依赖容器统一管理起来,在需要用到某个依赖项时由容器提供。这样就产生一个问题,依赖项什么时候实例化?是需要的时候重新创建;还是持久化一个实例;亦或是根据请求者的作用域提供实例?这就是依赖项的生命周期。

在依赖注入(DI)容器中,实例的创建可以根据配置和需要的时机进行不同的处理。一般来说,DI 容器在应用启动时会进行初始化,并根据需要预先创建和注册一些实例。具体的创建时机和生命周期可以通过配置来控制。

常见的实例生命周期包括:

  1. Singleton(单例):在整个应用程序生命周期中只创建一个实例,并在需要时重用。该实例通常在应用程序启动时被创建。
  2. Scoped(作用域):在每个作用域(例如每个请求)内创建一个实例,并在该作用域内重用。在每个作用域结束时,实例将被销毁。
  3. Transient(瞬态):每次请求时都创建一个新的实例。每个请求都会得到一个新的实例,并在请求完成后被销毁。

这些生命周期选项允许您根据应用程序的需要来管理实例的创建和生命周期。您可以在注册服务时指定所需的生命周期选项,例如使用 AddSingletonAddScopedAddTransient 方法。

其他#

除了 ASP.NET Core 默认提供的依赖注入容器,你还可以选择使用其他第三方的依赖注入容器。以下是一些常见的依赖注入容器:

  1. Autofac: Autofac 是一个流行的、功能强大的依赖注入容器,它提供了许多高级功能和扩展点,适用于各种应用场景。
  2. Unity: Unity 是 Microsoft 提供的一个轻量级的依赖注入容器,它具有良好的性能和可扩展性,并支持面向接口的开发。
  3. Ninject: Ninject 是另一个流行的依赖注入容器,它注重简洁性和易用性,提供了一种简单而灵活的方式来管理对象之间的依赖关系。
  4. StructureMap: StructureMap 是一个功能强大的依赖注入容器,它支持构造函数注入、属性注入和方法注入,并提供了许多高级功能,如拦截器和配置文件。

这些依赖注入容器都有各自的特点和优势,你可以根据项目需求和个人偏好选择适合的容器。在使用这些容器时,你需要按照它们的文档和示例进行相应的配置和使用。

需要注意的是,如果你选择使用第三方的依赖注入容器,你可能需要安装相应的 NuGet 包,并进行适当的配置。同时,你也需要了解这些容器的特定语法和用法,以正确地注册和解析依赖项。

总之,ASP.NET Core 默认的依赖注入容器已经足够满足大多数应用的需求。但如果你需要更高级的功能或特定的需求,可以考虑使用其他的依赖注入容器。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。