c# - 在依赖注入中使用不同的子类服务(在 Hangfire 任务调度器中)
问题描述
我正在使用 Hangfire 库设置 TaskScheduler。这些任务现在已注册为服务。有许多不同的工作/任务。在服务接口中有一个对每个任务实现的引用,如下所示:
using System.Threading.Tasks;
namespace Domain.Interfaces
{
public interface IService
{
Task DoStuff1();
Task DoStuff2();
//etc..
}
}
该服务在 Startup 类中注册,例如:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IService, Service>();
}
问题是现在我必须拥有服务类中每个任务的所有代码。我宁愿在服务接口中有 1 个引用,例如:
using System.Threading.Tasks;
namespace Domain.Interfaces
{
public interface IService
{
Task RunTask();
}
}
RunTask() 将被实际工作覆盖,每个单独的子类继承自 Service(并因此实现 IService),这样我可以保持我所有的任务代码良好和独立,但启动这些任务的调用由通用函数调用,如
_service.RunTask();
将所有不同的任务注册为单独的服务也不是一个好的选择。我不想要:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IService, Task1Service>();
services.AddTransient<IService, Task2Service>();
services.AddTransient<IService, Task3Service>();
//etcetc..
}
在 Hangfire 中,这些被注册为经常性工作,例如:
RecurringJob.AddOrUpdate<Worker>(system, w => w.DoStuff1(),appSettings.AuthInterval, TimeZoneInfo.Local);
RecurringJob.AddOrUpdate<Worker>(system, w => w.DoStuff2(),appSettings.AuthInterval, TimeZoneInfo.Local);
Worker 类将使用注入的 IService 执行任务,例如:
public async Task DoStuff1()
{
await _service.DoStuff1();
TextBuffer.WriteLine("DoStuff 1 was completed");
}
我想总体上保持这种结构,但我想指定实际注入的服务,以便 Worker 只需要包括:
public async Task DoStuff()
{
await _service.RunTask(); //would run the correct task based on the service injected ??
}
一个人应该怎么做?我对依赖注入概念相对较新,但认为我有一个基本的了解。我主要想:
- 限制需要在启动时注册的服务数量。
- 将任务拆分为单独的类以获得更好的项目结构
- 否则保持已经实现的一般依赖注入结构。
谢谢!
解决方案
您可以像不使用 Hangfire 一样为各个服务编写类。如果两个服务做不同的事情,那么它们可能应该是单独的类。
您可以根据其特定类型将它们注册为经常性工作:
RecurringJob.AddOrUpdate<SomeService>(
system,
s => s.DoWhateverThisServiceDoes(),
appSettings.AuthInterval,
TimeZoneInfo.Local);
RecurringJob.AddOrUpdate<OtherService>(
system,
o => o.DoOtherThing(),
appSettings.AuthInterval,
TimeZoneInfo.Local);
如果您决定不希望这两个服务作为单独的任务运行,而是作为单个任务的一部分运行,该怎么办?你也能做到:
public class CompositeService
{
private readonly SomeService _someService;
private readonly OtherService _otherService;
public CompositeService(SomeService someService, OtherService otherService)
{
_someService = someService;
_otherService = otherService;
}
public async Task DoCompositeThing()
{
await Task.WhenAll(
_someService.DoWhateverThisServiceDoes(),
_otherService.DoOtherThing());
}
}
RecurringJob.AddOrUpdate<CompositeService>(
system,
s => s.DoCompositeThing(),
appSettings.AuthInterval,
TimeZoneInfo.Local);
一个关键点是,无论您决定单独注册它们还是创建一个执行这两者的任务,您都不需要更改编写各个类的方式。要决定是让它们成为一个类还是单独的类,您可以应用单一职责原则,并考虑什么会使它们更易于理解和单元测试。就我个人而言,我倾向于编写更小的、独立的类。
另一个因素是各个类可能有自己的依赖关系,如下所示:
public class SomeService
{
private readonly IDependOnThis _dependency;
public SomeService(IDependOnThis dependency)
{
_dependency = dependency;
}
public async Task DoWhateverThisServiceDoes()
{
// uses _dependency for something
}
}
如果您将课程分开,这将更易于管理。如果它们都在一个类中,并且不同的方法依赖于不同的东西,那么您最终可能会将许多不相关的依赖项和方法塞进一个类中。没有必要这样做。即使我们希望所有功能都由一个类“编排”,我们仍然可以将其编写为单独的类,然后使用另一个类将它们组合在一起。
这是依赖注入模式的一大优点。它限制了我们一次需要阅读和理解的代码量。一个类可能有一个或多个依赖项。当您查看该类时,您不必考虑这些依赖项是否具有它们自己的依赖项。嵌套依赖关系可能存在层级。但是当我们查看一个类时,我们所要考虑的是它做了什么以及它如何使用它的依赖关系。(这也是它使单元测试更容易的原因。)
无论您创建多少类,您都需要使用 注册它们IServiceCollection
,以及它们的依赖项。您提到您不想注册多个服务,但这很正常。如果它变得很大并且失控,有办法将其分解。
推荐阅读
- r - Tidyverse 从字符条件中提取元素
- jquery - 悬停时更改 Shopify 上的 button_label
- javascript - Amazon Quicksight 与 React 本机集成的集成引发未处理的承诺拒绝,[ReferenceError: Can't find variable: HTMLElement]
- docker - Prometheus Operator 更改 scrape_interval
- python - 如何在所有 AWS 区域自动执行 EBS 加密?
- excel - 将 VBA 代码更改为按 B 列排序,而不是现在正在执行的操作
- python - 对于有间隙的网格数据,是否有替代 np.reshape 的方法?
- javascript - 仅使用 javascript 清除表单下拉列表
- spring - Spring Security 自定义身份验证提供程序的 authenticate() 方法不起作用
- java - 如何检查Java运行时环境中是否存在具有指定类名的重复类?