Использование различных сервисов подкласса для внедрения зависимостей (в планировщике задач Hangfire) - PullRequest
0 голосов
/ 04 июня 2019

Я настраиваю TaskScheduler, используя библиотеку Hangfire. Задачи теперь зарегистрированы как сервис. Есть много разных заданий / заданий. В интерфейсе сервиса есть ссылка на каждую реализацию Task, подобную этой:

    using System.Threading.Tasks;
    namespace Domain.Interfaces
    {
        public interface IService
        {
            Task DoStuff1();
            Task DoStuff2();
            //etc..
        }
    }

Эта услуга зарегистрирована в классе запуска, как:

    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 ??
    }

Как лучше всего это делать? Я относительно новичок в концепции внедрения зависимости, но думаю, что у меня есть базовое понимание. Я в основном хочу:

  1. ограничить количество услуг, которые необходимо будет зарегистрировать при запуске.
  2. Разделение задач на отдельные классы для улучшения структуры проекта
  3. в противном случае оставить общую структуру внедрения зависимостей уже реализованной.

Спасибо!

1 Ответ

1 голос
/ 04 июня 2019

Вы можете составлять классы для отдельных сервисов так же, как если бы вы не использовали 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 вместе с их зависимостями.Вы упомянули, что не хотите регистрировать несколько служб, но это нормально.Если оно становится большим и выходит из-под контроля, есть способы его разбить.

...