如何使用 .NET 内置 DI 容器“向下许多级别”配置依赖项注入

Dea*_*n P 0 c# design-patterns dependency-injection di-containers .net-core

我有一个控制台 .NET 核心应用程序,它使用该Microsoft.Extensions.DependencyInjection库作为依赖项注入框架。

我想使用这个框架来注入一个“向下”两个级别的依赖项,而不必在中间层多余地提及这个依赖项。我该怎么做呢?

目前,我注入依赖项的唯一方法是将其向下传递,直到需要为止。这是我的独立控制台应用程序,它演示了该要求。它是一个简单的程序,可以根据示例资产和负债金额计算一个人的净资产。(它实际上只是减去两个金额)。

Program.cs文件包含程序主入口点并注册依赖项。

程序.cs:

public class Program
{
    private static IServiceProvider _serviceProvider;
    public static void Main(string[] args)
    {
        RegisterServices();
        IServiceScope scope = _serviceProvider.CreateScope();
        scope.ServiceProvider.GetRequiredService<ConsoleApplication>().Run();
        DisposeServices();
    }

    private static void RegisterServices()
    {
        var services = new ServiceCollection();
        services.AddSingleton<ICalculator, Calculator>();
        services.AddSingleton<ConsoleApplication>();
        _serviceProvider = services.BuildServiceProvider(true);
    }

    private static void DisposeServices()
    {
        if (_serviceProvider == null)
        {
            return;
        }
        if (_serviceProvider is IDisposable)
        {
            ((IDisposable)_serviceProvider).Dispose();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

设置依赖项注入后,Program.cs运行该ConsoleApplication.cs Run方法。

ConsoleApplication.cs:

internal class ConsoleApplication
{
    private readonly ICalculator _calculator;
    public ConsoleApplication(ICalculator calculator)
    {
        _calculator = calculator;
    }

    public void Run()
    {
        Person person = new Person(_calculator)
        {
            Assets = 1000m,
            Liabilities = 300m
        };
        Console.WriteLine(person.GetNetWorth());
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码实例化了一个示例Person并调用该GetNetWorth方法。Person 类如下所示。

人.cs:

public class Person
{
    private readonly ICalculator _calculator;

    public Person(ICalculator calculator)
    {
        _calculator = calculator;
    }

    public decimal Assets {get; set;}
    public decimal Liabilities {get; set;}

    public decimal GetNetWorth()
    {
        decimal netWorth = _calculator.Subtract(Assets, Liabilities);
        return netWorth;
    }
}
Run Code Online (Sandbox Code Playgroud)

该类具有如下所示的Person依赖关系:Calculator

计算器.cs:

public interface ICalculator
{
    decimal Add(decimal num1, decimal num2);
    decimal Subtract(decimal num1, decimal num2);
    decimal Multiply(decimal num1, decimal num2);
    decimal Divide(decimal num1, decimal num2);
}

public class Calculator : ICalculator
{
    public decimal Add(decimal num1, decimal num2) => num1 + num2;
    public decimal Subtract(decimal num1, decimal num2) => num1 - num2;
    public decimal Multiply(decimal num1, decimal num2) => num1 * num2;
    public decimal Divide(decimal num1, decimal num2) => num1 / num2;
}
Run Code Online (Sandbox Code Playgroud)

鉴于上面的程序,您可以看到这里的问题是该类ConsoleApplication.cs有这行代码:

private readonly ICalculator _calculator;
public ConsoleApplication(ICalculator calculator)
{
    _calculator = calculator;
}
Run Code Online (Sandbox Code Playgroud)

该代码是多余的,应该避免,因为ConsoleApplication.cs 没有这种依赖性,因此不应该了解它的任何信息。Person我被迫包含它的唯一原因是将其传递到需要依赖项的一个级别。

通过上面的示例,如何调整program.cs以避免传递依赖项?我有一种感觉,我使用的Microsoft.Extensions.DependencyInjection框架完全错误。使用 DI 容器的全部目的就是为了规避这个问题。我可能根本就没有使用过 DI 容器,这样代码会更简单。

我读过很多询问类似问题的帖子。但是,它们不适合我的控制台应用程序、.NET 和使用Microsoft.Extensions.DependencyInjection;DI 框架的具体情况。

Nko*_*osi 5

这是一个设计问题。Person类根据运行时数据显示 SRP(单一职责原则)/SOC(关注点分离)违规和 DI 代码气味。(基于提供的简化示例)。

将运行时数据注入应用程序组件是一种代码味道。运行时数据应该流经已构造的对象图的方法调用。

参考依赖注入代码气味:将运行时数据注入组件

请注意如何使用运行时数据构造此类。

public class Person {
    private readonly ICalculator _calculator;

    public Person(ICalculator calculator) {
        _calculator = calculator;
    }

    public decimal Assets {get; set;} //<--Run-time data
    public decimal Liabilities {get; set;} //<--Run-time data

    public decimal GetNetWorth() {
        decimal netWorth = _calculator.Subtract(Assets, Liabilities);
        return netWorth;
    }
}
Run Code Online (Sandbox Code Playgroud)

创建一个单独的服务,其职责是在运行时获取一个人并计算他们的净值

//Run-time data
public class Person {        
    public decimal Assets {get; set;}
    public decimal Liabilities {get; set;}
}

//Service abstraction
public interface IPersonNetWorthService {
    decimal GetNetWorth(Person person);
}

//Service implementation
public class PersonNetWorthService : IPersonNetWorthService {
    private readonly ICalculator _calculator;

    public PersonNetWorthService(ICalculator calculator) {
        _calculator = calculator;
    }

    public decimal GetNetWorth(Person person) {
        decimal netWorth = _calculator.Subtract(person.Assets, person.Liabilities);
        return netWorth;
    }
}
Run Code Online (Sandbox Code Playgroud)

控制台应用程序现在可以干净地执行其功能,没有任何违规和代码异味

internal class ConsoleApplication
{
    private readonly IPersonNetWorthService calculator;
    public ConsoleApplication(IPersonNetWorthService calculator) {
        this.calculator = calculator;
    }

    public void Run() {
        Person person = new Person() {
            Assets = 1000m,
            Liabilities = 300m
        };
        Console.WriteLine(calculator.GetNetWorth(person));
    }
}
Run Code Online (Sandbox Code Playgroud)

记住注册所有依赖项

    private static void RegisterServices() {
        IServiceCollection services = new ServiceCollection();
        services.AddSingleton<IPersonNetWorthService, PersonNetWorthService>();
        services.AddSingleton<ICalculator, Calculator>();
        services.AddSingleton<ConsoleApplication>();
        _serviceProvider = services.BuildServiceProvider(true);
    }
Run Code Online (Sandbox Code Playgroud)