在依赖注入中使用不同的 sub-class 服务(在 Hangfire 任务调度器中)
Using different sub-class services in dependency injection (in Hangfire task-scheduler)
我正在使用 Hangfire 库设置一个 TaskScheduler。
这些任务现在注册为服务。有很多不同的jobs/tasks。
在服务接口中有对每个任务实现的引用,如下所示:
using System.Threading.Tasks;
namespace Domain.Interfaces
{
public interface IService
{
Task DoStuff1();
Task DoStuff2();
//etc..
}
}
此服务已在 Startup class 中注册,例如:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IService, Service>();
}
现在的问题是我必须拥有服务中每个任务的所有代码 class。
我宁愿在服务接口中只有 1 个引用,例如:
using System.Threading.Tasks;
namespace Domain.Interfaces
{
public interface IService
{
Task RunTask();
}
}
RunTask() 将被每个继承自 Service(并因此实现 IService)的单独子 class 中完成的实际工作覆盖
这样我就可以保持我所有的任务代码良好且独立,但是通过像
这样的通用函数调用来启动这些任务
_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 class 将使用注入的 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 ??
}
最好的解决办法是什么?
我对依赖注入概念比较陌生,但我认为我有基本的了解。
我主要想:
- 限制启动时需要注册的服务数量。
- 将任务拆分为单独的 classes 以获得更好的项目结构
- 否则保持已实现的通用依赖注入结构。
谢谢!
您可以像不使用 Hangfire 一样为各个服务编写 classes。如果两个服务做不同的事情,那么它们应该是分开的 classes。
您可以根据其具体类型将其注册为周期性工作:
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);
一个关键点是,无论您决定分别注册它们还是创建一个执行这两个任务的任务,您都不需要更改编写各个 classes 的方式。要决定是让它们成为一个 class 还是单独的 class es,你可以应用单一职责原则,并考虑什么会使它们更容易理解和单元测试。就个人而言,我倾向于编写更小的、单独的 classes.
另一个因素是各个 classes 可能有自己的依赖关系,如下所示:
public class SomeService
{
private readonly IDependOnThis _dependency;
public SomeService(IDependOnThis dependency)
{
_dependency = dependency;
}
public async Task DoWhateverThisServiceDoes()
{
// uses _dependency for something
}
}
如果您将 class 分开,这将更易于管理。如果它们都在一个 class 中并且不同的方法依赖于不同的事物,那么您最终可能会得到许多不相关的依赖项和方法塞进一个 class 中。没有必要那样做。即使我们想要一个 class 的所有功能 "orchestrated",我们仍然可以将其编写为单独的 class,然后使用另一个 class 将它们组合在一起。
这是依赖注入模式的一大优点。它限制了我们一次需要阅读和理解的代码量。一个 class 可能有一个依赖项或多个依赖项。当您查看 class 时,您不必考虑这些依赖项是否具有它们自己的依赖项。可能存在一层一层的嵌套依赖关系。但是,当我们查看一个 class 时,我们所要考虑的只是它的作用以及它如何使用其依赖项。 (这也是它使单元测试更容易的原因。)
无论您创建多少 classes,您都需要使用 IServiceCollection
注册它们以及它们的依赖项。您曾提到您不想注册多项服务,但这很正常。如果它变大并且 out-of-control 有办法分解它。
我正在使用 Hangfire 库设置一个 TaskScheduler。 这些任务现在注册为服务。有很多不同的jobs/tasks。 在服务接口中有对每个任务实现的引用,如下所示:
using System.Threading.Tasks;
namespace Domain.Interfaces
{
public interface IService
{
Task DoStuff1();
Task DoStuff2();
//etc..
}
}
此服务已在 Startup class 中注册,例如:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IService, Service>();
}
现在的问题是我必须拥有服务中每个任务的所有代码 class。 我宁愿在服务接口中只有 1 个引用,例如:
using System.Threading.Tasks;
namespace Domain.Interfaces
{
public interface IService
{
Task RunTask();
}
}
RunTask() 将被每个继承自 Service(并因此实现 IService)的单独子 class 中完成的实际工作覆盖 这样我就可以保持我所有的任务代码良好且独立,但是通过像
这样的通用函数调用来启动这些任务 _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 class 将使用注入的 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 ??
}
最好的解决办法是什么? 我对依赖注入概念比较陌生,但我认为我有基本的了解。 我主要想:
- 限制启动时需要注册的服务数量。
- 将任务拆分为单独的 classes 以获得更好的项目结构
- 否则保持已实现的通用依赖注入结构。
谢谢!
您可以像不使用 Hangfire 一样为各个服务编写 classes。如果两个服务做不同的事情,那么它们应该是分开的 classes。
您可以根据其具体类型将其注册为周期性工作:
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);
一个关键点是,无论您决定分别注册它们还是创建一个执行这两个任务的任务,您都不需要更改编写各个 classes 的方式。要决定是让它们成为一个 class 还是单独的 class es,你可以应用单一职责原则,并考虑什么会使它们更容易理解和单元测试。就个人而言,我倾向于编写更小的、单独的 classes.
另一个因素是各个 classes 可能有自己的依赖关系,如下所示:
public class SomeService
{
private readonly IDependOnThis _dependency;
public SomeService(IDependOnThis dependency)
{
_dependency = dependency;
}
public async Task DoWhateverThisServiceDoes()
{
// uses _dependency for something
}
}
如果您将 class 分开,这将更易于管理。如果它们都在一个 class 中并且不同的方法依赖于不同的事物,那么您最终可能会得到许多不相关的依赖项和方法塞进一个 class 中。没有必要那样做。即使我们想要一个 class 的所有功能 "orchestrated",我们仍然可以将其编写为单独的 class,然后使用另一个 class 将它们组合在一起。
这是依赖注入模式的一大优点。它限制了我们一次需要阅读和理解的代码量。一个 class 可能有一个依赖项或多个依赖项。当您查看 class 时,您不必考虑这些依赖项是否具有它们自己的依赖项。可能存在一层一层的嵌套依赖关系。但是,当我们查看一个 class 时,我们所要考虑的只是它的作用以及它如何使用其依赖项。 (这也是它使单元测试更容易的原因。)
无论您创建多少 classes,您都需要使用 IServiceCollection
注册它们以及它们的依赖项。您曾提到您不想注册多项服务,但这很正常。如果它变大并且 out-of-control 有办法分解它。