如何使用 .NET 内置 DI 容器配置依赖注入 "many levels down"
How to configure dependency injection "many levels down" using .NET built-in DI Container
我有一个控制台 .NET 核心应用程序,它使用 Microsoft.Extensions.DependencyInjection
库作为依赖注入框架。
我想使用这个框架来注入一个“向下”两层的依赖,而不必在中间层冗余地提到这个依赖。我该怎么做?
目前,我可以注入依赖项的唯一方法是将它向下传递,直到需要它为止。这是我的独立控制台应用程序,它演示了要求。它是一个简单的程序,可以根据示例资产和负债金额计算一个人的净资产。 (它实际上只是减去这两个数额)。
Program.cs
文件包含程序 Main 入口点并注册依赖项。
Program.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();
}
}
}
设置依赖项注入后,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());
}
}
上面的代码实例化了一个例子Person
并调用了GetNetWorth
方法。此人 class 如下所示。
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;
}
}
Person
class 对 Calculator
有依赖关系,如下所示:
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;
}
鉴于上面的程序,您可以看到这里的问题是 class ConsoleApplication.cs
有这行代码:
private readonly ICalculator _calculator;
public ConsoleApplication(ICalculator calculator)
{
_calculator = calculator;
}
该代码是多余的,应该避免,因为 ConsoleApplication.cs
没有 这种依赖性,因此不应该知道任何关于它的信息。我被迫包含它的唯一原因是将它传递到下一个级别 Person
, 需要依赖项。
对于上面的例子,我如何调整program.cs
以避免传递依赖?我有一种感觉,我使用的 Microsoft.Extensions.DependencyInjection
框架完全错误。使用 DI 容器的全部意义在于规避这个问题。我也可能根本没有使用 DI 容器,代码会更简单。
我已经阅读了许多询问类似问题的 SO 帖子。但是,它们不适合我的控制台应用程序、.NET 和使用 Microsoft.Extensions.DependencyInjection;
DI 框架的特定情况。
这是一个设计问题。 Person
class 根据 运行 时间数据显示 SRP(单一职责原则)/SOC(关注点分离)违规和 DI 代码味道。 (基于提供的简化示例)。
Injecting runtime data into your application components is a code smell. Runtime data should flow through the method calls of already-constructed object graphs.
引用Dependency Injection Code Smell: Injecting runtime data into components
注意这个 class 是如何用 run-time 数据构造的。
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-time 找到一个人并计算他们的净资产
//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;
}
}
控制台应用程序现在可以干净地执行其功能,没有任何违规和代码味道
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));
}
}
记得注册所有依赖项
private static void RegisterServices() {
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IPersonNetWorthService, PersonNetWorthService>();
services.AddSingleton<ICalculator, Calculator>();
services.AddSingleton<ConsoleApplication>();
_serviceProvider = services.BuildServiceProvider(true);
}
我有一个控制台 .NET 核心应用程序,它使用 Microsoft.Extensions.DependencyInjection
库作为依赖注入框架。
我想使用这个框架来注入一个“向下”两层的依赖,而不必在中间层冗余地提到这个依赖。我该怎么做?
目前,我可以注入依赖项的唯一方法是将它向下传递,直到需要它为止。这是我的独立控制台应用程序,它演示了要求。它是一个简单的程序,可以根据示例资产和负债金额计算一个人的净资产。 (它实际上只是减去这两个数额)。
Program.cs
文件包含程序 Main 入口点并注册依赖项。
Program.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();
}
}
}
设置依赖项注入后,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());
}
}
上面的代码实例化了一个例子Person
并调用了GetNetWorth
方法。此人 class 如下所示。
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;
}
}
Person
class 对 Calculator
有依赖关系,如下所示:
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;
}
鉴于上面的程序,您可以看到这里的问题是 class ConsoleApplication.cs
有这行代码:
private readonly ICalculator _calculator;
public ConsoleApplication(ICalculator calculator)
{
_calculator = calculator;
}
该代码是多余的,应该避免,因为 ConsoleApplication.cs
没有 这种依赖性,因此不应该知道任何关于它的信息。我被迫包含它的唯一原因是将它传递到下一个级别 Person
, 需要依赖项。
对于上面的例子,我如何调整program.cs
以避免传递依赖?我有一种感觉,我使用的 Microsoft.Extensions.DependencyInjection
框架完全错误。使用 DI 容器的全部意义在于规避这个问题。我也可能根本没有使用 DI 容器,代码会更简单。
我已经阅读了许多询问类似问题的 SO 帖子。但是,它们不适合我的控制台应用程序、.NET 和使用 Microsoft.Extensions.DependencyInjection;
DI 框架的特定情况。
这是一个设计问题。 Person
class 根据 运行 时间数据显示 SRP(单一职责原则)/SOC(关注点分离)违规和 DI 代码味道。 (基于提供的简化示例)。
Injecting runtime data into your application components is a code smell. Runtime data should flow through the method calls of already-constructed object graphs.
引用Dependency Injection Code Smell: Injecting runtime data into components
注意这个 class 是如何用 run-time 数据构造的。
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-time 找到一个人并计算他们的净资产
//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;
}
}
控制台应用程序现在可以干净地执行其功能,没有任何违规和代码味道
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));
}
}
记得注册所有依赖项
private static void RegisterServices() {
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IPersonNetWorthService, PersonNetWorthService>();
services.AddSingleton<ICalculator, Calculator>();
services.AddSingleton<ConsoleApplication>();
_serviceProvider = services.BuildServiceProvider(true);
}