.NET Worker 服务中的构造函数注入
Constructor Injection in .NET Worker Service
我花了几个月的时间使用 ASP.NET Core MVC 编写代码。现在我正在尝试工作人员服务,但遇到了依赖注入的一些问题。
我可以在 Program.cs 中注册服务并在 Worker.cs 中使用它(见下文)。当您需要像这样的 class 结构中的服务时,我的问题就出现了:
- 在 Worker.cs 中创建对象 1
- 对象 1 有对象 2 作为 属性
- 对象 2 需要实例化服务
当然,我可以将服务向下传递到整个层次结构,但在这种情况下,我看不到服务的价值。
似乎我只能在工作服务级别上使用依赖注入。在 MVC 中,您可以简单地注册一个服务并将其作为参数传递给 Controller 的构造函数。这里的优点是您永远不必像 Worker.cs 级别那样显式调用构造函数。
我在尝试访问连接字符串和设置时遇到了这个问题,但我想这同样适用于记录器。
Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
services.AddTransient<ITestService,TestService>();
});
Worker.cs
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public readonly ITestService TestServ;
public Worker(ILogger<Worker> logger, ITestService test)
{
_logger = logger;
TestServ = test;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
//this works
_logger.LogInformation("TestService: " + TestServ.GetData());
//is this necessary?
TestClassServ test = new TestClassServ(TestServ);
_logger.LogInformation("TestService: " + test.GetData());
await Task.Delay(1000, stoppingToken);
}
}
}
Class 和服务
public interface ITestService
{
string GetData();
}
public class TestService : ITestService
{
public const string teststring = "service";
public string GetData()
{
return teststring;
}
}
public class TestClassServ
{
public ITestService TestServ { get; set; }
public TestClassServ(ITestService testServ)
{
TestServ = testServ;
}
public string GetData()
{
return TestServ.GetData();
}
}
经过数周的谷歌搜索和尝试,我决定向您寻求帮助:
- 甚至可以在 class 层次结构的较低级别注入服务而不从顶部向下传递服务吗?
- 你会怎么做?
- 我的问题是否源于程序的架构?
It seems like I can use Dependency Injection only on the level of the worker service.
该框架当然能够创建包含深层对象图的工作器 类,因此当然可以将依赖项注入多个层次;即使与工人 类.
一起工作
但这里有一个问题,即 Worker 类 仅由框架解析一次,基本上使它们成为单例。这意味着,即使您可以向它们注入依赖项,它的直接和间接依赖项也不应该是 Scoped 依赖项。
我假设这就是您正在努力解决的问题,因为在大多数情况下,为了让工作人员做任何有用的事情,它需要与数据库对话。这通常意味着使用 Entity Framework,并且其 DbContext
始终是 Scoped。在 Singleton 中注入它是 a really bad idea. The DbContext
becomes a Captive Dependency.
这意味着,您必须在应用程序的生命周期内从容器中解析它们,而不是将依赖项注入到 worker 的构造函数中。这将 Worker 耦合到 DI 容器,但是当您使 Worker 成为 Composition Root.
的一部分时,这不是问题
不过,这确实需要您在解析服务之前创建一个新范围。这是一个例子:
public class Worker : BackgroundService
{
// NOTE: Logger can be safely injected; loggers are always singletons
private readonly ILogger<Worker> _logger;
private readonly IServiceProvider _provider;
public Worker(ILogger<Worker> logger, IServiceProvider provider)
{
_logger = logger;
_provider = provider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await using (var scope = _provider.CreateScope())
{
var service = scope.GetRequiredService<ITestService>();
service.GoDoSomethingUseful();
}
await Task.Delay(1000, stoppingToken);
}
}
}
Is my problem rooted in the architecture of the program?
不,就您的代码示例所显示的而言不是。只要您继续访问 DI 容器(或其 IServiceProvider
抽象)在您的组合根中 ,就可以了。
我花了几个月的时间使用 ASP.NET Core MVC 编写代码。现在我正在尝试工作人员服务,但遇到了依赖注入的一些问题。
我可以在 Program.cs 中注册服务并在 Worker.cs 中使用它(见下文)。当您需要像这样的 class 结构中的服务时,我的问题就出现了:
- 在 Worker.cs 中创建对象 1
- 对象 1 有对象 2 作为 属性
- 对象 2 需要实例化服务
- 对象 1 有对象 2 作为 属性
当然,我可以将服务向下传递到整个层次结构,但在这种情况下,我看不到服务的价值。 似乎我只能在工作服务级别上使用依赖注入。在 MVC 中,您可以简单地注册一个服务并将其作为参数传递给 Controller 的构造函数。这里的优点是您永远不必像 Worker.cs 级别那样显式调用构造函数。 我在尝试访问连接字符串和设置时遇到了这个问题,但我想这同样适用于记录器。
Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
services.AddTransient<ITestService,TestService>();
});
Worker.cs
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public readonly ITestService TestServ;
public Worker(ILogger<Worker> logger, ITestService test)
{
_logger = logger;
TestServ = test;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
//this works
_logger.LogInformation("TestService: " + TestServ.GetData());
//is this necessary?
TestClassServ test = new TestClassServ(TestServ);
_logger.LogInformation("TestService: " + test.GetData());
await Task.Delay(1000, stoppingToken);
}
}
}
Class 和服务
public interface ITestService
{
string GetData();
}
public class TestService : ITestService
{
public const string teststring = "service";
public string GetData()
{
return teststring;
}
}
public class TestClassServ
{
public ITestService TestServ { get; set; }
public TestClassServ(ITestService testServ)
{
TestServ = testServ;
}
public string GetData()
{
return TestServ.GetData();
}
}
经过数周的谷歌搜索和尝试,我决定向您寻求帮助:
- 甚至可以在 class 层次结构的较低级别注入服务而不从顶部向下传递服务吗?
- 你会怎么做?
- 我的问题是否源于程序的架构?
It seems like I can use Dependency Injection only on the level of the worker service.
该框架当然能够创建包含深层对象图的工作器 类,因此当然可以将依赖项注入多个层次;即使与工人 类.
一起工作但这里有一个问题,即 Worker 类 仅由框架解析一次,基本上使它们成为单例。这意味着,即使您可以向它们注入依赖项,它的直接和间接依赖项也不应该是 Scoped 依赖项。
我假设这就是您正在努力解决的问题,因为在大多数情况下,为了让工作人员做任何有用的事情,它需要与数据库对话。这通常意味着使用 Entity Framework,并且其 DbContext
始终是 Scoped。在 Singleton 中注入它是 a really bad idea. The DbContext
becomes a Captive Dependency.
这意味着,您必须在应用程序的生命周期内从容器中解析它们,而不是将依赖项注入到 worker 的构造函数中。这将 Worker 耦合到 DI 容器,但是当您使 Worker 成为 Composition Root.
的一部分时,这不是问题不过,这确实需要您在解析服务之前创建一个新范围。这是一个例子:
public class Worker : BackgroundService
{
// NOTE: Logger can be safely injected; loggers are always singletons
private readonly ILogger<Worker> _logger;
private readonly IServiceProvider _provider;
public Worker(ILogger<Worker> logger, IServiceProvider provider)
{
_logger = logger;
_provider = provider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await using (var scope = _provider.CreateScope())
{
var service = scope.GetRequiredService<ITestService>();
service.GoDoSomethingUseful();
}
await Task.Delay(1000, stoppingToken);
}
}
}
Is my problem rooted in the architecture of the program?
不,就您的代码示例所显示的而言不是。只要您继续访问 DI 容器(或其 IServiceProvider
抽象)在您的组合根中 ,就可以了。