From b22c4011562f654f3fb5c00004dc984dfb0374c5 Mon Sep 17 00:00:00 2001 From: Mikhail Kuznetsov Date: Tue, 13 Jun 2023 13:57:55 +1000 Subject: [PATCH] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8=20Apps,=20?= =?UTF-8?q?Scheduler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AIHIT/MappingProfiles/RequestToDomain.cs | 5 +- PARR.AIHIT/AIHIT/Syncher.cs | 290 +++++++++++++++--- PARR.DAL/Context/DataContext.cs | 5 + PARR.DAL/Contracts/ApplicationTypesEnum.cs | 9 + PARR.DAL/Models/Application.cs | 29 ++ PARR.DAL/Models/ApplicationInHost.cs | 28 ++ PARR.DAL/Models/ApplicationType.cs | 20 ++ PARR.DAL/Models/Host.cs | 6 +- PARR.DAL/Models/Job.cs | 2 +- PARR.DAL/Models/Scheduler.cs | 19 ++ .../Services/Implementations/HostService.cs | 2 +- PARR.Worker/PARR.Worker/Program.cs | 5 +- 12 files changed, 360 insertions(+), 60 deletions(-) create mode 100644 PARR.DAL/Contracts/ApplicationTypesEnum.cs create mode 100644 PARR.DAL/Models/Application.cs create mode 100644 PARR.DAL/Models/ApplicationInHost.cs create mode 100644 PARR.DAL/Models/ApplicationType.cs create mode 100644 PARR.DAL/Models/Scheduler.cs diff --git a/PARR.AIHIT/AIHIT/MappingProfiles/RequestToDomain.cs b/PARR.AIHIT/AIHIT/MappingProfiles/RequestToDomain.cs index 5747325a..1862ffc5 100644 --- a/PARR.AIHIT/AIHIT/MappingProfiles/RequestToDomain.cs +++ b/PARR.AIHIT/AIHIT/MappingProfiles/RequestToDomain.cs @@ -14,10 +14,7 @@ namespace PARR.AIHIT.MappingProfiles .ForMember(d => d.LinkEK, o => o.MapFrom(s => s.Attributes!["СВЯЗАННЫЙ_ЭК"]!.Value.Trim())) .ForMember(d => d.Status, o => o.MapFrom(s => s.Attributes!["СТАТУС"]!.Value.Trim())) .ForMember(d => d.WorkGroup, o => o.MapFrom(s => (s.Attributes!["РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК"] != null) ? s.Attributes!["РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК"]!.Value.Trim() : "")) - .ForMember(d => d.Responsible, o => o.MapFrom(s => s.Attributes!["ОТВЕТСТВЕННЫЙ_ЗА_ЭК"]!.Value.Trim())) - .ForMember(d => d.OS, o => o.MapFrom(s => (s.Attributes!["ОС"] != null) ? s.Attributes!["ОС"]!.Value.Trim() : "")) - .ForMember(d => d.DB, o => o.MapFrom(s => (s.Attributes!["СУБД"] != null) ? s.Attributes!["СУБД"]!.Value.Trim() : "")) - .ForMember(d => d.APP, o => o.MapFrom(s => (s.Attributes!["СП"] != null) ? s.Attributes!["СП"]!.Value.Trim() : "")); + .ForMember(d => d.Responsible, o => o.MapFrom(s => s.Attributes!["ОТВЕТСТВЕННЫЙ_ЗА_ЭК"]!.Value.Trim())); //CreateMap() // .ForMember(d => d.HostName, o => o.MapFrom(s => s.HostName)) diff --git a/PARR.AIHIT/AIHIT/Syncher.cs b/PARR.AIHIT/AIHIT/Syncher.cs index 17002932..126636a6 100644 --- a/PARR.AIHIT/AIHIT/Syncher.cs +++ b/PARR.AIHIT/AIHIT/Syncher.cs @@ -4,6 +4,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using MimeKit; using PARR.AIHIT.Settings; +using PARR.DAL.Contracts; +using PARR.DAL.Extensions; using PARR.DAL.Models; using PARR.DAL.Services.Interfaces; using PARR.Mail.Services; @@ -19,31 +21,48 @@ namespace PARR.AIHIT private readonly SyncherSettings syncherSettings; private readonly IHostService hostService; private readonly IMapper mapper; + private readonly IApplicationService appService; + private readonly IApplicationTypeService appTypeService; + private readonly IApplicationInHostService appInHostService; + + public List AppTypes { get; private set; } public Syncher( ILogger logger, IMailService mailService, SyncherSettings syncherSettings, IHostService hostService, - IMapper mapper + IMapper mapper, + IApplicationService appService, + IApplicationTypeService appTypeService, + IApplicationInHostService appInHostService ) { this.hostService = hostService; this.mapper = mapper; + this.appService = appService; + this.appTypeService = appTypeService; + this.appInHostService = appInHostService; this.logger = logger; this.mailService = mailService; this.syncherSettings = syncherSettings; } + private async Task GetAppTypesAsync() + { + AppTypes = await appTypeService.Get().ToListAsync(); + } + public async Task InvokeAsync() { - var hosts = await hostService.Get().ToListAsync(); + await GetAppTypesAsync(); + var messages = await mailService.CheckMailAsync(SearchQuery.NotSeen); if (!messages.Any()) { return; } - var filtered = messages.Where(m => syncherSettings.IncludeAddressesArray.Any(i => m.Envelope.From.Mailboxes.First().Address == i)).ToList(); + var filtered = messages.Where(m => syncherSettings.IncludeAddressesArray.Any(i => m.Envelope.From.Mailboxes.FirstOrDefault()?.Address == i)).ToList(); var lastMessage = filtered.OrderByDescending(f => f.Date).First(); var attachments = await mailService.GetAttachmentsAsync(lastMessage); foreach (var attachment in attachments) @@ -54,7 +73,7 @@ namespace PARR.AIHIT } else { - var stream = new MemoryStream(); + using var stream = new MemoryStream(); ((MimePart)attachment).Content.DecodeTo(stream); if (stream.Position > 0) stream.Position = 0; @@ -71,14 +90,33 @@ namespace PARR.AIHIT var ip = vm?.Attributes?["IP_АДРЕС"]?.Value.Trim(); if (ip == null) continue; var regionalEK = vm!.Attributes!["РЕГИОНАЛЬНЫЙ_ЭК"]!.Value.Trim(); - var eK = vm!.Attributes!["СВЯЗАННЫЙ_ЭК"]!.Value.Trim(); - if (!eK.StartsWith("ВРТ")) continue; + var linkEK = vm!.Attributes!["СВЯЗАННЫЙ_ЭК"]!.Value.Trim(); + if (!linkEK.StartsWith("ВРТ")) continue; - var foundHost = await hostService.GetHostByIPAndRegionalEKAsync(ip, regionalEK); + // var foundHost = await hostService.GetHostAsync(ip, linkEK, regionalEK); + var foundHost = await hostService.GetHostWithAppsAsync(ip, linkEK, regionalEK); var mappedHost = mapper.Map(vm); - if (foundHost != null) + var hostApplications = await GetAndFillApplicationsAsync(vm); + + if (foundHost == null) { + // создаем + if (!await hostService.CreateAsync(mappedHost) || !await hostService.CommitAsync()) + { + logger.LogError($"Не удалось создать узел {mappedHost.IP} {mappedHost.RegionalEK}"); + continue; + } + else + logger.LogInformation($"----- Создан Host: {mappedHost.Id}, {mappedHost.LinkEK} -----"); + + + // Синхронизация Applications + await SyncApplicationAndHostAsync(foundHost, hostApplications); + } + else + { + // обновляем var isChanged = false; if (!foundHost.RegionalEK!.Equals(mappedHost.RegionalEK)) { @@ -104,32 +142,33 @@ namespace PARR.AIHIT foundHost.Responsible = mappedHost.Responsible; isChanged = true; } - else if (!foundHost.OS!.Equals(mappedHost.OS)) - { - foundHost.OS = mappedHost.OS; - isChanged = true; - } - else if (!foundHost.DB!.Equals(mappedHost.DB)) - { - foundHost.DB = mappedHost.DB; - isChanged = true; - } - else if (!foundHost.APP!.Equals(mappedHost.APP)) - { - foundHost.APP = mappedHost.APP; - isChanged = true; - } - foundHost.DateModified = DateTimeOffset.UtcNow; ; - - if (isChanged && !await hostService.CommitAsync()) - logger.LogError($"Не удалось обновить узел {mappedHost.IP} {mappedHost.RegionalEK}"); - else logger.LogInformation($"Обновлён узел {mappedHost.IP} {mappedHost.RegionalEK}"); + //else if (!foundHost.OS!.Equals(mappedHost.OS)) + //{ + // foundHost.OS = mappedHost.OS; + // isChanged = true; + //} + //else if (!foundHost.DB!.Equals(mappedHost.DB)) + //{ + // foundHost.DB = mappedHost.DB; + // isChanged = true; + //} + //else if (!foundHost.APP!.Equals(mappedHost.APP)) + //{ + // foundHost.APP = mappedHost.APP; + // isChanged = true; + //} - } - else - { - if (!await hostService.CreateAsync(mappedHost) || !await hostService.CommitAsync()) - logger.LogError($"Не удалось создать узел {mappedHost.IP} {mappedHost.RegionalEK}"); + if (isChanged) + { + foundHost.DateModified = DateTimeOffset.UtcNow; + if (!await hostService.CommitAsync()) + logger.LogError($"Не удалось обновить узел {mappedHost.IP} {mappedHost.RegionalEK}"); + else + logger.LogInformation($"----- Обновлён Host: {mappedHost.Id}, {mappedHost.LinkEK} -----"); + } + + // Синхронизация Applications + await SyncApplicationAndHostAsync(foundHost, hostApplications, true); } @@ -168,22 +207,181 @@ namespace PARR.AIHIT } } - private bool IsChanged(Host host, Host mappedHost) + private async Task SyncApplicationAndHostAsync(Host? host, List hostApplications, bool isUpdateDateModified = false) { - if (host == null) return true; - if (mappedHost == null) return true; - if (!host.IP.Equals(mappedHost.IP)) return true; - if (!host.RegionalEK!.Equals(mappedHost.RegionalEK)) return true; - if (!host.LinkEK!.Equals(mappedHost.LinkEK)) return true; - if (!host.Status!.Equals(mappedHost.Status)) return true; - if (!host.WorkGroup!.Equals(mappedHost.WorkGroup)) return true; - if (!host.Responsible!.Equals(mappedHost.Responsible)) return true; - if (!host.OS!.Equals(mappedHost.OS)) return true; - if (!host.DB!.Equals(mappedHost.DB)) return true; - if (!host.APP!.Equals(mappedHost.APP)) return true; - return false; + var isUpdated = false; + if (host == null) + return; + + logger.LogInformation($"--- Host: {host.Id}, {host.LinkEK} начало синхронизации приложений ({hostApplications.Count()}) ---"); + + foreach (var changedHostApp in hostApplications) + { + var exist = host.ApplicationsInHosts.FirstOrDefault(a => a.ApplicationId == changedHostApp.Id); + + if (exist == null) + { + //add + logger.LogInformation($"Host: {host.Id}, {host.LinkEK} добавление нового приложения Application {changedHostApp.Name}"); + + var newAppHost = new ApplicationInHost { Id = Guid.NewGuid(), ApplicationId = changedHostApp.Id, HostId = host.Id }; + if (!await appInHostService.CreateAsync(newAppHost)) + logger.LogError($"Не удалось создать ApplicationInHost {changedHostApp.Name}, host: {host.Id}, {host.LinkEK}"); + + isUpdated = true; + } + } + + foreach (var app in host.ApplicationsInHosts) + { + var appToRemove = hostApplications.FirstOrDefault(a => a.Id == app.Id); + + if (appToRemove == null) + { + //todo remove appToRemove + var obj = await appInHostService.Get().FirstOrDefaultAsync(t => t.ApplicationId == app.Id && t.HostId == host.Id); + if (obj != null) + { + logger.LogInformation($"Host: {host.Id}, {host.LinkEK} удаление неактуального приложения Application {app.Application?.Name}"); + appInHostService.Delete(obj); + + isUpdated = true; + } + } + } + + if (isUpdated && isUpdateDateModified) + host.DateModified = DateTimeOffset.UtcNow; + + if (!await appInHostService.CommitAsync()) + logger.LogError($"Не удалось обновить ApplicationInHost для Host: {host.LinkEK}, {host.Id}"); + + logger.LogInformation($"=== Host: {host.Id}, {host.LinkEK} конец синхронизации приложений ==="); + } + private async Task> GetAndFillApplicationsAsync(XmlNode vm) + { + logger.LogInformation($"--- Начало парсинга данных о программном обеспечении(СП, БД, ОС) из Xml документа от АИХ ИТ ---"); + var applications = new List(); + + //TODO GetAppFromXML(vm?.Attributes?["СП"]?.Value).ForEach(async item => await CreateAppIfNotExistAsync(item, ApplicationTypesEnum.APP)); + //GetDBFromXML(vm?.Attributes?["СУБД"]?.Value).ForEach(async item => await CreateAppIfNotExistAsync(item, ApplicationTypesEnum.DB)); + //GetOSFromXML(vm?.Attributes?["ОС"]?.Value).ForEach(async item => await CreateAppIfNotExistAsync(item, ApplicationTypesEnum.OS)); + var appList = GetAppFromXML(vm?.Attributes?["СП"]?.Value); + foreach (var app in appList) + { + var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.APP); + if (created != null) + applications.Add(created); + } + var dbList = GetAppFromXML(vm?.Attributes?["СУБД"]?.Value); + foreach (var db in dbList) + { + var created = await CreateAppIfNotExistAsync(db, ApplicationTypesEnum.DB); + if (created != null) + applications.Add(created); + } + var osList = GetAppFromXML(vm?.Attributes?["ОС"]?.Value); + foreach (var os in appList) + { + var created = await CreateAppIfNotExistAsync(os, ApplicationTypesEnum.OS); + if (created != null) + applications.Add(created); + } + + logger.LogInformation($"=== Конец парсинга данных о программном обеспечении(СП, БД, ОС) из Xml документа от АИХ ИТ ==="); + + return applications; + } + + private List GetAppFromXML(string? appField) + { + if (string.IsNullOrEmpty(appField)) + return new List(); + + var appList = appField.Split(";").Distinct().ToList(); + //appList.ForEach(item => item.Trim()); + + return appList; + } + + private List GetDBFromXML(string? dbField) + { + if (string.IsNullOrEmpty(dbField)) + return new List(); + + var dbList = dbField.Split(";").Distinct().ToList(); + //dbList.ForEach(item => item.Trim()); + + return dbList; + } + + private List GetOSFromXML(string? osField) + { + if (string.IsNullOrEmpty(osField)) + return new List(); + + var osList = osField.Split(";").Distinct().ToList(); + //osList.ForEach(item => item.Trim()); + + return osList; + } + + private async Task CreateAppIfNotExistAsync(string appName, ApplicationTypesEnum type)//Application application) + { + if (string.IsNullOrEmpty(appName)) return null; + + var existApp = await appService.GetByName(appName); + if (existApp != null) + return existApp; + + //Создаем + var appType = GetAppTypeByName(type); + if (appType == null) + { + logger.LogError($"Не удалось создать запись в таблице Applications: {appName}, так как не получил AppType: {type.ToString()}"); + return null; + } + + var app = new DAL.Models.Application + { + Id = Guid.NewGuid(), + Name = appName.Trim(), + ApplicationTypeId = appType.Id + }; + + if (!await appService.CreateAsync(app) || !await appService.CommitAsync()) + logger.LogError($"Не удалось создать запись в таблице Application: {appName}, {app.ToJson()}"); + else + logger.LogInformation($"Создана запись а таблице Application: {appName}, {app.ToJson()}"); + + return await appService.GetAsync(app.Id); + } + + private ApplicationType? GetAppTypeByName(ApplicationTypesEnum type) + { + var existType = AppTypes.FirstOrDefault(t => t.Name == type.ToString()); + if (existType == null) + logger.LogError($"Не найден AppType: {type}"); + + return existType; + } + + + //private bool IsChanged(Host host, Host mappedHost) + //{ + // if (host == null) return true; + // if (mappedHost == null) return true; + // if (!host.IP.Equals(mappedHost.IP)) return true; + // if (!host.RegionalEK!.Equals(mappedHost.RegionalEK)) return true; + // if (!host.LinkEK!.Equals(mappedHost.LinkEK)) return true; + // if (!host.Status!.Equals(mappedHost.Status)) return true; + // if (!host.WorkGroup!.Equals(mappedHost.WorkGroup)) return true; + // if (!host.Responsible!.Equals(mappedHost.Responsible)) return true; + // return false; + //} + private static string EncodeString(string str) { byte[] bStr = Encoding.GetEncoding("koi8r").GetBytes(str); diff --git a/PARR.DAL/Context/DataContext.cs b/PARR.DAL/Context/DataContext.cs index 0e83b450..82f5a5d8 100644 --- a/PARR.DAL/Context/DataContext.cs +++ b/PARR.DAL/Context/DataContext.cs @@ -14,5 +14,10 @@ namespace PARR.DAL.Context public DbSet Hosts { get; set; } public DbSet Jobs { get; set; } public DbSet JobJournals { get; set; } + public DbSet Applications { get; set; } + public DbSet ApplicationTypes { get; set; } + public DbSet ApplicationsInHosts { get; set; } + public DbSet Schedulers { get; set; } + } } diff --git a/PARR.DAL/Contracts/ApplicationTypesEnum.cs b/PARR.DAL/Contracts/ApplicationTypesEnum.cs new file mode 100644 index 00000000..1e2ef089 --- /dev/null +++ b/PARR.DAL/Contracts/ApplicationTypesEnum.cs @@ -0,0 +1,9 @@ +namespace PARR.DAL.Contracts +{ + public enum ApplicationTypesEnum + { + APP, + OS, + DB + } +} diff --git a/PARR.DAL/Models/Application.cs b/PARR.DAL/Models/Application.cs new file mode 100644 index 00000000..d060c032 --- /dev/null +++ b/PARR.DAL/Models/Application.cs @@ -0,0 +1,29 @@ +using PARR.DAL.Models.Base; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PARR.DAL.Models +{ + [Table("Applications")] + public class Application : IBase + { + [Key] + public Guid Id { get; set; } + public DateTimeOffset DateCreated { get; set; } + public DateTimeOffset? DateModified { get; set; } + public required string Name { get; set; } + + public Guid ApplicationTypeId { get; set; } + [ForeignKey(nameof(ApplicationTypeId))] + public ApplicationType? ApplicationType { get; set; } + + + public ICollection ApplicationsInHosts { get; set; } = new HashSet(); + } +} +} diff --git a/PARR.DAL/Models/ApplicationInHost.cs b/PARR.DAL/Models/ApplicationInHost.cs new file mode 100644 index 00000000..deb6710d --- /dev/null +++ b/PARR.DAL/Models/ApplicationInHost.cs @@ -0,0 +1,28 @@ +using PARR.DAL.Models.Base; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PARR.DAL.Models +{ + [Table("ApplicationsinHosts")] + public class ApplicationInHost : IBase + { + public Guid Id { get; set; } + public DateTimeOffset DateCreated { get; set; } + public DateTimeOffset? DateModified { get; set; } + + public Guid ApplicationId { get; set; } + + [ForeignKey(nameof(ApplicationId))] + public Application? Application { get; set; } + + public Guid HostId { get; set; } + + [ForeignKey(nameof(HostId))] + public Host? Host { get; set; } + } +} diff --git a/PARR.DAL/Models/ApplicationType.cs b/PARR.DAL/Models/ApplicationType.cs new file mode 100644 index 00000000..8bc3b113 --- /dev/null +++ b/PARR.DAL/Models/ApplicationType.cs @@ -0,0 +1,20 @@ +using PARR.DAL.Models.Base; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PARR.DAL.Models +{ + [Table("ApplicationTypes")] + public class ApplicationType : IBase + { + public Guid Id { get; set; } + public DateTimeOffset DateCreated { get; set; } + public DateTimeOffset? DateModified { get; set; } + public required string Name { get; set; } + public string? Description { get; set; } + } +} diff --git a/PARR.DAL/Models/Host.cs b/PARR.DAL/Models/Host.cs index 9fc9bdb5..fcc116ad 100644 --- a/PARR.DAL/Models/Host.cs +++ b/PARR.DAL/Models/Host.cs @@ -11,19 +11,17 @@ namespace PARR.DAL.Models public Guid Id { get; set; } public DateTimeOffset DateCreated { get; set; } public DateTimeOffset? DateModified { get; set; } - public string HostName { get; set; } + public string HostName { get; set; } public required string IP { get; set; } public string? RegionalEK { get; set; } public string? LinkEK { get; set; } public string? Status { get; set; } public string? WorkGroup { get; set; } public string? Responsible { get; set; } - public string? OS { get; set; } - public string? DB { get; set; } - public string? APP { get; set; } public ICollection JobJournals { get; set; } = new HashSet(); + public ICollection ApplicationsInHosts { get; set; } = new HashSet(); } } diff --git a/PARR.DAL/Models/Job.cs b/PARR.DAL/Models/Job.cs index fa463235..5de518aa 100644 --- a/PARR.DAL/Models/Job.cs +++ b/PARR.DAL/Models/Job.cs @@ -21,7 +21,7 @@ namespace PARR.DAL.Models public Guid JobCategoryId { get; set; } [ForeignKey(nameof(JobCategoryId))] public JobCategory? JobCategorye { get; set; } - + public Guid JobTypeId { get; set; } [ForeignKey(nameof(JobTypeId))] public JobType? JobType { get; set; } diff --git a/PARR.DAL/Models/Scheduler.cs b/PARR.DAL/Models/Scheduler.cs new file mode 100644 index 00000000..a52f305d --- /dev/null +++ b/PARR.DAL/Models/Scheduler.cs @@ -0,0 +1,19 @@ +using PARR.DAL.Models.Base; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace PARR.DAL.Models +{ + [Table("Schedulers")] + public class Scheduler : IBase + { + [Key] + public Guid Id { get; set; } + public DateTimeOffset DateCreated { get; set; } + public DateTimeOffset? DateModified { get; set; } + public string? Name { get; set; } + public int Frequency { get; set; } + public required DateTimeOffset StartAt { get; set; } + public bool IsEnabled { get; set; } + } +} diff --git a/PARR.DAL/Services/Implementations/HostService.cs b/PARR.DAL/Services/Implementations/HostService.cs index ee06a6a5..3bacc878 100644 --- a/PARR.DAL/Services/Implementations/HostService.cs +++ b/PARR.DAL/Services/Implementations/HostService.cs @@ -21,7 +21,7 @@ namespace PARR.DAL.Services.Implementations this.logger = logger; } - public async Task GetHostByIPAndRegionalEKAsync(string IP, string RegionalEK) + public async Task GetHostWithAppsAsync(string IP, string RegionalEK)//TODO { try { diff --git a/PARR.Worker/PARR.Worker/Program.cs b/PARR.Worker/PARR.Worker/Program.cs index 67211d6d..506146f8 100644 --- a/PARR.Worker/PARR.Worker/Program.cs +++ b/PARR.Worker/PARR.Worker/Program.cs @@ -1,4 +1,3 @@ -using Microsoft.Extensions.Configuration; using PARR.AIHIT; using PARR.DAL; using PARR.Mail; @@ -12,10 +11,8 @@ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { - IConfiguration configuration = hostContext.Configuration; - var workerSettings = new WorkerSettings(); - workerSettings = hostContext.Configuration.GetSection(nameof(WorkerSettings)).Get(); + hostContext.Configuration.GetSection(nameof(WorkerSettings)).Bind(workerSettings); services.AddSingleton(workerSettings);