diff --git a/AsbCloudApp/Data/UserRoleDto.cs b/AsbCloudApp/Data/UserRoleDto.cs index aa525c91..8d761a85 100644 --- a/AsbCloudApp/Data/UserRoleDto.cs +++ b/AsbCloudApp/Data/UserRoleDto.cs @@ -29,7 +29,7 @@ namespace AsbCloudApp.Data /// /// Включенные роли /// - public virtual ICollection Roles { get; set; } + public virtual IEnumerable Roles { get; set; } /// /// Пользователи в роли diff --git a/AsbCloudApp/Repositories/IFileRepository.cs b/AsbCloudApp/Repositories/IFileRepository.cs index a05cf909..a62aa0d5 100644 --- a/AsbCloudApp/Repositories/IFileRepository.cs +++ b/AsbCloudApp/Repositories/IFileRepository.cs @@ -11,7 +11,7 @@ namespace AsbCloudApp.Repositories /// /// Сервис доступа к файлам /// - public interface IFileRepository : ICrudService + public interface IFileRepository : ICrudRepository { /// /// Получение файлов по скважине diff --git a/AsbCloudApp/Repositories/IUserRepository.cs b/AsbCloudApp/Repositories/IUserRepository.cs index 4f6b0fac..f23dc67b 100644 --- a/AsbCloudApp/Repositories/IUserRepository.cs +++ b/AsbCloudApp/Repositories/IUserRepository.cs @@ -7,7 +7,7 @@ namespace AsbCloudApp.Repositories /// /// Репозиторий пользователей /// - public interface IUserRepository : ICrudService + public interface IUserRepository : ICrudRepository { /// /// Получить список всех прав пользователя (включая наследование групп) diff --git a/AsbCloudApp/Repositories/IUserRoleRepository.cs b/AsbCloudApp/Repositories/IUserRoleRepository.cs index a2c25704..d20a42cb 100644 --- a/AsbCloudApp/Repositories/IUserRoleRepository.cs +++ b/AsbCloudApp/Repositories/IUserRoleRepository.cs @@ -10,7 +10,7 @@ namespace AsbCloudApp.Repositories /// /// Разрешения на доступ к данным /// - public interface IUserRoleRepository : ICrudService + public interface IUserRoleRepository : ICrudRepository { /// /// получить dto по названиям diff --git a/AsbCloudApp/Services/FileService.cs b/AsbCloudApp/Services/FileService.cs index 9b7ca156..d361cf38 100644 --- a/AsbCloudApp/Services/FileService.cs +++ b/AsbCloudApp/Services/FileService.cs @@ -73,7 +73,7 @@ namespace AsbCloudApp.Services /// /// /// - public async Task SaveAsync(int idWell, int? idUser, int idCategory, + public async Task SaveAsync(int idWell, int? idUser, int idCategory, string fileFullName, Stream fileStream, CancellationToken token) { //save info to db @@ -93,7 +93,7 @@ namespace AsbCloudApp.Services string filePath = fileStorageRepository.MakeFilePath(idWell, idCategory, fileFullName, fileId); await fileStorageRepository.SaveFileAsync(filePath, fileStream, token); - return await GetOrDefaultAsync(fileId, token); + return (await GetOrDefaultAsync(fileId, token))!; } /// diff --git a/AsbCloudApp/Services/IBackgroundWorkerService.cs b/AsbCloudApp/Services/IBackgroundWorkerService.cs deleted file mode 100644 index 2ccc47ab..00000000 --- a/AsbCloudApp/Services/IBackgroundWorkerService.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudApp.Services -{ - /// - /// Сервис выстраивает очередь из фоновых задач. Ограничивает количество одновременно выполняющихся задач. - /// - public interface IBackgroundWorkerService - { - /// - /// Проверка, есть ли задача в очереди - /// - /// идентификатор задачи - /// - bool Contains(string id); - - /// - /// Добавляет в очередь задач новую задачу - /// - /// идентификатор задачи - /// делегат - /// id задачи в очереди - string Enqueue(string id, Func func); - - /// - /// Добавляет в очередь задач новую задачу - /// - /// - /// - string Enqueue(Func func); - - /// - /// Добавляет в очередь задач новую задачу - /// - /// идентификатор задачи - /// - /// - /// - string Enqueue(string id, Func func, Func onError); - - /// - /// Пробуем удалить задачу по идентификатору - /// - /// - /// - bool TryRemove(string id); - } -} \ No newline at end of file diff --git a/AsbCloudApp/Services/ICrudService.cs b/AsbCloudApp/Services/ICrudRepository.cs similarity index 98% rename from AsbCloudApp/Services/ICrudService.cs rename to AsbCloudApp/Services/ICrudRepository.cs index bbb7fce8..f402ebb9 100644 --- a/AsbCloudApp/Services/ICrudService.cs +++ b/AsbCloudApp/Services/ICrudRepository.cs @@ -10,7 +10,7 @@ namespace AsbCloudApp.Services /// Сервис получения, добавления, изменения, удаления данных /// /// - public interface ICrudService + public interface ICrudRepository where TDto : Data.IId { /// diff --git a/AsbCloudApp/Services/IDrillParamsService.cs b/AsbCloudApp/Services/IDrillParamsService.cs index e95e74b8..edb5067f 100644 --- a/AsbCloudApp/Services/IDrillParamsService.cs +++ b/AsbCloudApp/Services/IDrillParamsService.cs @@ -8,7 +8,7 @@ namespace AsbCloudApp.Services /// /// The параметры бурения service. /// - public interface IDrillParamsService : ICrudService + public interface IDrillParamsService : ICrudRepository { /// /// default параметры бурения diff --git a/AsbCloudApp/Services/ILimitingParameterService.cs b/AsbCloudApp/Services/ILimitingParameterService.cs index 3e82eba1..5f752a62 100644 --- a/AsbCloudApp/Services/ILimitingParameterService.cs +++ b/AsbCloudApp/Services/ILimitingParameterService.cs @@ -19,6 +19,12 @@ namespace AsbCloudApp.Services /// /// Task> GetStatAsync(LimitingParameterRequest request, CancellationToken token); + + /// + /// Получение списка ограничений + /// + /// + Dictionary GetLimitingParameteraNames(); } #nullable disable } diff --git a/AsbCloudApp/Services/IReportService.cs b/AsbCloudApp/Services/IReportService.cs index c66861b7..e5e24648 100644 --- a/AsbCloudApp/Services/IReportService.cs +++ b/AsbCloudApp/Services/IReportService.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; namespace AsbCloudApp.Services { +#nullable enable /// /// Сервис рапортов /// @@ -16,7 +17,6 @@ namespace AsbCloudApp.Services /// int ReportCategoryId { get; } - // TODO: rename this method /// /// Поставить рапорт в очередь на формирование /// @@ -28,7 +28,7 @@ namespace AsbCloudApp.Services /// /// /// - string CreateReport(int idWell, int idUser, int stepSeconds, + string EnqueueCreateReportWork(int idWell, int idUser, int stepSeconds, int format, DateTime begin, DateTime end, Action handleReportProgress); @@ -49,7 +49,7 @@ namespace AsbCloudApp.Services /// /// /// - DatesRangeDto GetDatesRangeOrDefault(int idWell); + DatesRangeDto? GetDatesRangeOrDefault(int idWell); /// /// Список готовых рапортов @@ -58,5 +58,7 @@ namespace AsbCloudApp.Services /// /// Task> GetAllReportsByWellAsync(int idWell, CancellationToken token); + +#nullable disable } } diff --git a/AsbCloudApp/Services/IRepositoryWellRelated.cs b/AsbCloudApp/Services/IRepositoryWellRelated.cs index 6ce2af0e..f608fd04 100644 --- a/AsbCloudApp/Services/IRepositoryWellRelated.cs +++ b/AsbCloudApp/Services/IRepositoryWellRelated.cs @@ -11,7 +11,7 @@ namespace AsbCloudApp.Services /// Для сущностей относящихся к скважине /// /// - public interface IRepositoryWellRelated : ICrudService + public interface IRepositoryWellRelated : ICrudRepository where Tdto : IId, IWellRelated { /// diff --git a/AsbCloudApp/Services/IWellService.cs b/AsbCloudApp/Services/IWellService.cs index b8f9531c..7eef310e 100644 --- a/AsbCloudApp/Services/IWellService.cs +++ b/AsbCloudApp/Services/IWellService.cs @@ -9,7 +9,7 @@ namespace AsbCloudApp.Services /// /// сервис скважин /// - public interface IWellService : ICrudService + public interface IWellService : ICrudRepository { /// /// сервис телеметрии diff --git a/AsbCloudDb/Model/AsbCloudDbContext.cs b/AsbCloudDb/Model/AsbCloudDbContext.cs index 4e91eafe..8354d87f 100644 --- a/AsbCloudDb/Model/AsbCloudDbContext.cs +++ b/AsbCloudDb/Model/AsbCloudDbContext.cs @@ -60,20 +60,32 @@ namespace AsbCloudDb.Model public DbSet Record50 => Set(); public DbSet Record60 => Set(); public DbSet Record61 => Set(); - + + public static int ReferenceCount { get; private set; } + public AsbCloudDbContext() : base() { + ReferenceCount++; } public AsbCloudDbContext(DbContextOptions options) : base(options) { + ReferenceCount++; + } + + ~AsbCloudDbContext() + { + ReferenceCount--; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + if (!optionsBuilder.IsConfigured) - optionsBuilder.UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True"); + optionsBuilder.UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True" + //, builder=>builder.EnableRetryOnFailure(2, System.TimeSpan.FromMinutes(1)) + ); } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/AsbCloudInfrastructure/Background/BackgroundWorker.cs b/AsbCloudInfrastructure/Background/BackgroundWorker.cs new file mode 100644 index 00000000..9453e6a9 --- /dev/null +++ b/AsbCloudInfrastructure/Background/BackgroundWorker.cs @@ -0,0 +1,98 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Background +{ +# nullable enable + /// + /// Сервис для фонового выполнения работы + /// + public class BackgroundWorker : BackgroundService + { + private static readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10); + private static readonly TimeSpan minDelay = TimeSpan.FromSeconds(2); + private static readonly TimeSpan exceptionHandleTimeout = TimeSpan.FromSeconds(2); + private readonly IServiceProvider serviceProvider; + private readonly WorkQueue workQueue = new WorkQueue(); + public string? CurrentWorkId; + public BackgroundWorker(IServiceProvider serviceProvider) + { + this.serviceProvider = serviceProvider; + } + + /// + /// Добавление задачи в очередь. + /// Не периодические задачи будут выполняться вперед. + /// + /// + /// Id mast be unique + public void Push(WorkBase work) + { + workQueue.Push(work); + } + + /// + /// Проверяет наличие работы с указанным Id + /// + /// + /// + public bool Contains(string id) + { + return workQueue.Contains(id); + } + + /// + /// Удаление работы по ID + /// + /// + /// + public bool Delete(string id) + { + return workQueue.Delete(id); + } + + protected override async Task ExecuteAsync(CancellationToken token) + { + while (!token.IsCancellationRequested) + { + var dateStart = DateTime.Now; + var work = workQueue.Pop(); + if (work is null) + { + await Task.Delay(executePeriod, token); + continue; + } + CurrentWorkId = work.Id; + using var scope = serviceProvider.CreateScope(); + + try + { + Trace.TraceInformation($"Backgroud work:\"{work.Id}\" start."); + var task = work.ActionAsync(work.Id, scope.ServiceProvider, token); + await task.WaitAsync(work.Timeout, token); + + work.ExecutionTime = DateTime.Now - dateStart; + Trace.TraceInformation($"Backgroud work:\"{work.Id}\" done. ExecutionTime: {work.ExecutionTime:hh\\:mm\\:ss\\.fff}"); + } + catch (Exception exception) + { + Trace.TraceError($"Backgroud work:\"{work.Id}\" throw exception: {exception.Message}"); + if (work.OnErrorAsync is not null) + { + using var task = Task.Run( + async () => await work.OnErrorAsync(work.Id, exception, token), + token); + await task.WaitAsync(exceptionHandleTimeout, token); + } + } + CurrentWorkId = null; + await Task.Delay(minDelay, token); + } + } + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Background/WorkBase.cs b/AsbCloudInfrastructure/Background/WorkBase.cs new file mode 100644 index 00000000..ce07a2fa --- /dev/null +++ b/AsbCloudInfrastructure/Background/WorkBase.cs @@ -0,0 +1,69 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Background +{ +#nullable enable + /// + /// Класс разовой работы. + /// Разовая работа приоритетнее периодической. + /// + public class WorkBase + { + /// + /// Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки. + /// + public string Id { get; private set; } + + /// + /// Делегат работы. + /// + /// Параметры: + /// + /// + /// string + /// Id Идентификатор работы + /// + /// + /// IServiceProvider + /// Поставщик сервисов + /// + /// + /// CancellationToken + /// Токен отмены задачи + /// + /// + /// + /// + internal Func ActionAsync { get; set; } + + /// + /// Делегат обработки ошибки. + /// Не должен выполняться долго. + /// + public Func? OnErrorAsync { get; set; } + + /// + /// максимально допустимое время выполнения работы + /// + public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1); + + /// + /// Фактическое время успешного выполнения работы + /// + public TimeSpan? ExecutionTime { get; internal set; } + + /// + /// Время последнего запуска + /// + public DateTime LastStart { get; set; } + + public WorkBase(string id, Func actionAsync) + { + Id = id; + ActionAsync = actionAsync; + } + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Background/WorkPeriodic.cs b/AsbCloudInfrastructure/Background/WorkPeriodic.cs new file mode 100644 index 00000000..ae29ee78 --- /dev/null +++ b/AsbCloudInfrastructure/Background/WorkPeriodic.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Background +{ +#nullable enable + /// + /// Класс периодической работы. + /// + public class WorkPeriodic : WorkBase + { + /// + /// Период выполнения задачи + /// + public TimeSpan Period { get; set; } + + /// + /// Время следующего запуска + /// + public DateTime NextStart => LastStart + Period; + + /// + /// Класс периодической работы + /// + /// Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки + /// Делегат работы + /// Период выполнения задачи + public WorkPeriodic(string id, Func actionAsync, TimeSpan period) + : base(id, actionAsync) + { + Period = period; + } + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Background/WorkQueue.cs b/AsbCloudInfrastructure/Background/WorkQueue.cs new file mode 100644 index 00000000..5521d373 --- /dev/null +++ b/AsbCloudInfrastructure/Background/WorkQueue.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AsbCloudInfrastructure.Background +{ +#nullable enable + /// + /// + /// Очередь работ + /// + /// Не периодические задачи будут возвращаться первыми, как самые приоритетные. + /// + class WorkQueue + { + private Queue Primary = new(8); + private readonly List Periodic = new(8); + + /// + /// Добавление работы. + /// + /// + /// Id mast be unique + public void Push(WorkBase work) + { + if (Periodic.Any(w => w.Id == work.Id)) + throw new ArgumentException("work.Id is not unique", nameof(work)); + + if (Primary.Any(w => w.Id == work.Id)) + throw new ArgumentException("work.Id is not unique", nameof(work)); + + if (work is WorkPeriodic workPeriodic) + { + Periodic.Add(workPeriodic); + return; + } + + Primary.Enqueue(work); + } + + /// + /// Удаление работы по ID + /// + /// + /// + public bool Delete(string id) + { + var workPeriodic = Periodic.FirstOrDefault(w => w.Id == id); + if (workPeriodic is not null) + { + Periodic.Remove(workPeriodic); + return true; + } + + var work = Primary.FirstOrDefault(w => w.Id == id); + if (work is not null) + { + Primary = new Queue(Primary.Where(w => w.Id != id)); + return true; + } + + return false; + } + + public bool Contains(string id) + { + var result = Periodic.Any(w => w.Id == id) || Primary.Any(w => w.Id == id); + return result; + } + + /// + /// + /// Возвращает приоритетную задачу. + /// + /// + /// Если приоритетные закончились, то ищет ближайшую периодическую. + /// Если до старта ближайшей периодической работы меньше 20 сек, + /// то этой задаче устанавливается время последнего запуска в now и она возвращается. + /// Если больше 20 сек, то возвращается null. + /// + /// + /// + /// + public WorkBase? Pop() + { + if (Primary.Any()) + return Primary.Dequeue(); + + var work = GetNextPeriodic(); + if (work is null || work.NextStart > DateTime.Now) + return null; + + work.LastStart = DateTime.Now; + return work; + } + + private WorkPeriodic? GetNextPeriodic() + { + var work = Periodic + .OrderBy(w => w.NextStart) + .ThenByDescending(w => w.Period) + .FirstOrDefault(); + return work; + } + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 90c1bf5c..fa3e5e25 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -6,6 +6,7 @@ using AsbCloudApp.Services; using AsbCloudApp.Services.Subsystems; using AsbCloudDb.Model; using AsbCloudDb.Model.Subsystems; +using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Repository; using AsbCloudInfrastructure.Services; using AsbCloudInfrastructure.Services.DailyReport; @@ -97,16 +98,13 @@ namespace AsbCloudInfrastructure services.AddScoped(provider => provider.GetService()); services.AddScoped(); - services.AddHostedService(); - services.AddHostedService(); - services.AddHostedService(); services.AddSingleton(new WitsInfoService()); services.AddSingleton(new InstantDataRepository()); services.AddSingleton(provider=> TelemetryDataCache.GetInstance(configuration)); services.AddSingleton(provider=> TelemetryDataCache.GetInstance(configuration)); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(provider => ReduceSamplingService.GetInstance(configuration)); services.AddTransient(); @@ -139,33 +137,33 @@ namespace AsbCloudInfrastructure services.AddTransient(); // admin crud services: - services.AddTransient, CrudServiceBase>(s => - new CrudCacheServiceBase( + services.AddTransient, CrudCacheRepositoryBase>(s => + new CrudCacheRepositoryBase( s.GetService(), s.GetService(), dbSet => dbSet.Include(t => t.Well))); // может быть включен в сервис TelemetryService - services.AddTransient, DrillParamsService>(); - services.AddTransient, CrudCacheServiceBase>(s => - new CrudCacheServiceBase( + services.AddTransient, DrillParamsService>(); + services.AddTransient, CrudCacheRepositoryBase>(s => + new CrudCacheRepositoryBase( s.GetService(), s.GetService(), dbSet => dbSet.Include(d => d.Clusters))); - services.AddTransient, CrudCacheServiceBase>(s => - new CrudCacheServiceBase( + services.AddTransient, CrudCacheRepositoryBase>(s => + new CrudCacheRepositoryBase( s.GetService(), s.GetService(), dbSet => dbSet.Include(c => c.CompanyType))); - services.AddTransient, CrudCacheServiceBase>(); - services.AddTransient, CrudCacheServiceBase>(s => - new CrudCacheServiceBase( + services.AddTransient, CrudCacheRepositoryBase>(); + services.AddTransient, CrudCacheRepositoryBase>(s => + new CrudCacheRepositoryBase( s.GetService(), s.GetService(), dbSet => dbSet .Include(c => c.Wells) .Include(c => c.Deposit))); // может быть включен в сервис ClusterService - services.AddTransient, CrudCacheServiceBase>(); + services.AddTransient, CrudCacheRepositoryBase>(); services.AddTransient(); services.AddTransient(); @@ -174,10 +172,10 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); // Subsystem service - services.AddTransient, CrudCacheServiceBase>(); + services.AddTransient, CrudCacheRepositoryBase>(); services.AddTransient(); - services.AddTransient, CrudCacheServiceBase>(); + services.AddTransient, CrudCacheRepositoryBase>(); // TelemetryData services services.AddTransient, TelemetryDataSaubService>(); diff --git a/AsbCloudInfrastructure/Helper.cs b/AsbCloudInfrastructure/Helper.cs deleted file mode 100644 index 01fc97b5..00000000 --- a/AsbCloudInfrastructure/Helper.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; - -namespace AsbCloudInfrastructure -{ - public static class Helper - { - public static T Max(params T[] items) - where T : IComparable - { - var count = items.Length; - if (count < 1) - throw new ArgumentException("Count of params must be greater than 1"); - - var max = items[0]; - for (var i = 1; i < count; i++) - if (max.CompareTo(items[i]) < 0) - max = items[i]; - - return max; - } - - public static T Min(params T[] items) - where T : IComparable - { - var count = items.Length; - if (count < 1) - throw new ArgumentException("Count of params must be greater than 1"); - - var min = items[0]; - for (var i = 1; i < count; i++) - if (min.CompareTo(items[i]) > 0) - min = items[i]; - - return min; - } - - public static (T min, T max) MinMax(params T[] items) - where T : IComparable - { - var count = items.Length; - if (count < 1) - throw new ArgumentException("Count of params must be greater than 1"); - - var min = items[0]; - var max = items[0]; - for (var i = 1; i < count; i++) - if (max.CompareTo(items[i]) < 0) - max = items[i]; - else if (min.CompareTo(items[i]) > 0) - min = items[i]; - - return (min, max); - } - } -} diff --git a/AsbCloudInfrastructure/MapsterExtension.cs b/AsbCloudInfrastructure/MapsterExtension.cs deleted file mode 100644 index 94ed630c..00000000 --- a/AsbCloudInfrastructure/MapsterExtension.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Mapster -{ - public static class MapsterExtension - { - //public static IEnumerable Adapt(this IEnumerable sourceList) - //{ - // return sourceList.Select(item => item.Adapt()); - //} - - - } -} diff --git a/AsbCloudInfrastructure/CacheExtentions.cs b/AsbCloudInfrastructure/MemoryCacheExtentions.cs similarity index 100% rename from AsbCloudInfrastructure/CacheExtentions.cs rename to AsbCloudInfrastructure/MemoryCacheExtentions.cs diff --git a/AsbCloudInfrastructure/ReportDataSourcePgCloud.cs b/AsbCloudInfrastructure/ReportDataSourcePgCloud.cs index b42f44cd..f85a3108 100644 --- a/AsbCloudInfrastructure/ReportDataSourcePgCloud.cs +++ b/AsbCloudInfrastructure/ReportDataSourcePgCloud.cs @@ -10,7 +10,7 @@ namespace AsbCloudInfrastructure { public class ReportDataSourcePgCloud : IReportDataSource { - private readonly AsbCloudDbContext context; + private readonly IAsbCloudDbContext context; private readonly int? idTelemetry; private readonly WellInfoReport info; @@ -25,7 +25,7 @@ namespace AsbCloudInfrastructure {3, "Информация"}, }; - public ReportDataSourcePgCloud(AsbCloudDbContext context, int idWell) + public ReportDataSourcePgCloud(IAsbCloudDbContext context, int idWell) { this.context = context; @@ -65,6 +65,7 @@ namespace AsbCloudInfrastructure public AnalyzeResult Analyze() { + // TODO: Replace by linq methods. var messagesStat = (from item in context.TelemetryMessages where item.IdTelemetry == idTelemetry group item.DateTime by item.IdTelemetry into g diff --git a/AsbCloudInfrastructure/Repository/CacheBase.cs b/AsbCloudInfrastructure/Repository/CacheBase.cs new file mode 100644 index 00000000..042f2517 --- /dev/null +++ b/AsbCloudInfrastructure/Repository/CacheBase.cs @@ -0,0 +1,62 @@ +using AsbCloudDb.Model; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Repository +{ +#nullable enable + public class CacheBase : QueryContainer + where TEntity : class, AsbCloudDb.Model.IId + { + protected readonly IMemoryCache memoryCache; + protected string CacheTag = typeof(TEntity).Name; + protected TimeSpan CacheOlescence = TimeSpan.FromMinutes(5); + + public CacheBase(IAsbCloudDbContext context, IMemoryCache memoryCache) + : base(context) + { + this.memoryCache = memoryCache; + } + + public CacheBase(IAsbCloudDbContext context, IMemoryCache memoryCache, Func, IQueryable> makeQuery) + : base(context, makeQuery) + { + this.memoryCache = memoryCache; + } + + protected virtual void DropCache() + => memoryCache.Remove(CacheTag); + + protected virtual IEnumerable GetCache() + { + var cache = memoryCache.GetOrCreate(CacheTag, cacheEntry => + { + cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence; + cacheEntry.SlidingExpiration = CacheOlescence; + + var entities = this.GetQuery().ToArray(); + return entities; + }); + return cache; + } + + protected virtual Task> GetCacheAsync(CancellationToken token) + { + var cache = memoryCache.GetOrCreateAsync(CacheTag, async (cacheEntry) => + { + cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence; + cacheEntry.SlidingExpiration = CacheOlescence; + + var entities = await this.GetQuery().ToArrayAsync(token); + return entities.AsEnumerable(); + }); + return cache; + } + } +#nullable disable +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Repository/CrudCacheRepositoryBase.cs b/AsbCloudInfrastructure/Repository/CrudCacheRepositoryBase.cs new file mode 100644 index 00000000..ebea5d96 --- /dev/null +++ b/AsbCloudInfrastructure/Repository/CrudCacheRepositoryBase.cs @@ -0,0 +1,114 @@ +using AsbCloudApp.Services; +using AsbCloudDb.Model; +using Mapster; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Repository +{ +#nullable enable + /// + /// CRUD + /// + /// + /// + public class CrudCacheRepositoryBase : CacheBase, ICrudRepository + where TDto : AsbCloudApp.Data.IId + where TEntity : class, IId + { + protected int KeySelector(TEntity entity) => entity.Id; + protected readonly ICrudRepository crudServiceBase; + + public CrudCacheRepositoryBase(IAsbCloudDbContext dbContext, IMemoryCache memoryCache) + : base(dbContext, memoryCache) + { + crudServiceBase = new CrudRepositoryBase(dbContext); + } + + public CrudCacheRepositoryBase(IAsbCloudDbContext dbContext, IMemoryCache memoryCache, Func, IQueryable> makeQuery) + : base(dbContext, memoryCache, makeQuery) + { + crudServiceBase = new CrudRepositoryBase(dbContext, makeQuery); + } + + /// + public virtual async Task InsertAsync(TDto newItem, CancellationToken token) + { + var result = await crudServiceBase.InsertAsync(newItem, token); + if (result > 0) + DropCache(); + return result; + } + + /// + public virtual async Task InsertRangeAsync(IEnumerable dtos, CancellationToken token) + { + var result = await crudServiceBase.InsertRangeAsync(dtos, token); + if (result > 0) + DropCache(); + return result; + } + + /// + public virtual async Task UpdateAsync(TDto dto, CancellationToken token) + { + var result = await crudServiceBase.UpdateAsync(dto, token); + if (result > 0) + DropCache(); + return result; + } + + /// + public async Task> GetAllAsync(CancellationToken token) + { + var cache = await GetCacheAsync(token); + var dtos = cache.Select(Convert); + return dtos; + } + + /// + /// + /// + /// + /// + public TDto? GetOrDefault(int id) + { + var cache = GetCache(); + var cacheItem = cache.FirstOrDefault(d => d.Id == id); + if (cacheItem is null) + return default; + var dto = Convert(cacheItem); + return dto; + } + + /// + public async Task GetOrDefaultAsync(int id, CancellationToken token) + { + var cache = await GetCacheAsync(token); + var cacheItem = cache.FirstOrDefault(d => d.Id == id); + if (cacheItem is null) + return default; + var dto = Convert(cacheItem); + return dto; + } + + /// + public virtual async Task DeleteAsync(int id, CancellationToken token) + { + var result = await crudServiceBase.DeleteAsync(id, token); + if (result > 0) + DropCache(); + return result; + } + + protected virtual TDto Convert(TEntity src) => src.Adapt(); + + protected virtual TEntity Convert(TDto src) => src.Adapt(); + } +#nullable disable +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs deleted file mode 100644 index 2624d39c..00000000 --- a/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs +++ /dev/null @@ -1,140 +0,0 @@ -using AsbCloudDb.Model; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Caching.Memory; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudInfrastructure.Repository -{ -#nullable enable - /// - /// CRUD - /// - /// - /// - public class CrudCacheServiceBase : CrudServiceBase - where TDto : AsbCloudApp.Data.IId - where TEntity : class, IId - { - protected string CacheTag = typeof(TDto).Name; - protected TimeSpan CacheOlescence = TimeSpan.FromMinutes(5); - private readonly IMemoryCache memoryCache; - - protected int KeySelector(TEntity entity) => entity.Id; - - public CrudCacheServiceBase(IAsbCloudDbContext dbContext, IMemoryCache memoryCache) - : base(dbContext) - { - this.memoryCache = memoryCache; - } - - public CrudCacheServiceBase(IAsbCloudDbContext dbContext, IMemoryCache memoryCache, Func, IQueryable> makeQuery) - : base(dbContext, makeQuery) - { - this.memoryCache = memoryCache; - } - - /// - public override async Task InsertAsync(TDto newItem, CancellationToken token) - { - var result = await base.InsertAsync(newItem, token); - if (result > 0) - DropCache(); - return result; - } - - /// - public override async Task InsertRangeAsync(IEnumerable dtos, CancellationToken token) - { - var result = await base.InsertRangeAsync(dtos, token); - if (result > 0) - DropCache(); - return result; - } - - /// - public override async Task> GetAllAsync(CancellationToken token) - { - var cache = await GetCacheAsync(token); - return cache; - } - - /// - /// - /// - /// - /// - public override TDto? GetOrDefault(int id) - { - var cache = GetCache(); - return cache.FirstOrDefault(d => d.Id == id); - } - - /// - public override async Task GetOrDefaultAsync(int id, CancellationToken token) - { - var cache = await GetCacheAsync(token); - return cache.FirstOrDefault(d => d.Id == id); - } - - /// - public override async Task UpdateAsync(TDto dto, CancellationToken token) - { - var result = await base.UpdateAsync(dto, token); - if (result > 0) - DropCache(); - return result; - } - - /// - public override async Task UpdateRangeAsync(IEnumerable dtos, CancellationToken token) - { - var result = await base.UpdateRangeAsync(dtos, token); - if (result > 0) - DropCache(); - return result; - } - - /// - public override async Task DeleteAsync(int id, CancellationToken token) - { - var result = await base.DeleteAsync(id, token); - if (result > 0) - DropCache(); - return result; - } - - protected virtual Task> GetCacheAsync(CancellationToken token) - { - var cache = memoryCache.GetOrCreateAsync(CacheTag, async (cacheEntry) => { - cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence; - cacheEntry.SlidingExpiration = CacheOlescence; - - var entities = await GetQuery().ToArrayAsync(token); - var dtos = entities.Select(Convert); - return dtos.ToArray().AsEnumerable(); - }); - return cache; - } - - protected virtual IEnumerable GetCache() - { - var cache = memoryCache.GetOrCreate(CacheTag, cacheEntry => { - cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence; - cacheEntry.SlidingExpiration= CacheOlescence; - - var entities = GetQuery().ToArray(); - var dtos = entities.Select(Convert); - return dtos.ToArray(); - }); - return cache; - } - - protected virtual void DropCache() - => memoryCache.Remove(CacheTag); - } -#nullable disable -} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Repository/CrudServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs similarity index 71% rename from AsbCloudInfrastructure/Repository/CrudServiceBase.cs rename to AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs index 67b750cc..da51ab32 100644 --- a/AsbCloudInfrastructure/Repository/CrudServiceBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudRepositoryBase.cs @@ -16,33 +16,22 @@ namespace AsbCloudInfrastructure.Repository /// /// /// - public class CrudServiceBase : ICrudService + public class CrudRepositoryBase : QueryContainer, ICrudRepository where TDto : AsbCloudApp.Data.IId where TEntity : class, IId { - protected readonly IAsbCloudDbContext dbContext; - protected readonly DbSet dbSet; - protected readonly Func> GetQuery; + public CrudRepositoryBase(IAsbCloudDbContext context) + : base(context) + { } - public CrudServiceBase(IAsbCloudDbContext context) - { - dbContext = context; - dbSet = context.Set(); - GetQuery = () => dbSet; - } - - public CrudServiceBase(IAsbCloudDbContext context, Func, IQueryable> makeQuery) - { - dbContext = context; - dbSet = context.Set(); - GetQuery = () => makeQuery(dbSet); - } + public CrudRepositoryBase(IAsbCloudDbContext context, Func, IQueryable> makeQuery) + : base(context, makeQuery) + { } /// public virtual async Task> GetAllAsync(CancellationToken token = default) { var entities = await GetQuery() - //.OrderBy(e => e.Id) .AsNoTracking() .ToListAsync(token) .ConfigureAwait(false); @@ -117,7 +106,7 @@ namespace AsbCloudInfrastructure.Repository .ConfigureAwait(false); if (existingEntity is null) - return ICrudService.ErrorIdNotFound; + return ICrudRepository.ErrorIdNotFound; var entity = Convert(item); var entry = dbSet.Update(entity); @@ -126,29 +115,6 @@ namespace AsbCloudInfrastructure.Repository return entry.Entity.Id; } - public virtual async Task UpdateRangeAsync(IEnumerable dtos, CancellationToken token) - { - var ids = dtos.Select(d => d.Id); - var existingEntities = await dbSet - .AsNoTracking() - .Where(d => ids.Contains(d.Id)) - .Select(d => d.Id) - .ToListAsync(token) - .ConfigureAwait(false); - - if (ids.Count() > existingEntities.Count) - return ICrudService.ErrorIdNotFound; - - foreach (var dto in dtos) - { - var entity = Convert(dto); - var entry = dbSet.Update(entity); - } - - var affected = await dbContext.SaveChangesAsync(token); - return affected; - } - /// public virtual Task DeleteAsync(int id, CancellationToken token = default) { @@ -156,7 +122,7 @@ namespace AsbCloudInfrastructure.Repository .AsNoTracking() .FirstOrDefault(e => e.Id == id); if (entity == default) - return Task.FromResult(ICrudService.ErrorIdNotFound); + return Task.FromResult(ICrudRepository.ErrorIdNotFound); var entry = dbSet.Remove(entity); var affected = dbContext.SaveChangesAsync(token); entry.State = EntityState.Detached; diff --git a/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs index a7eb3305..9ac2c8b7 100644 --- a/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Repository { #nullable enable - public class CrudWellRelatedCacheServiceBase : CrudCacheServiceBase, IRepositoryWellRelated + public class CrudWellRelatedCacheServiceBase : CrudCacheRepositoryBase, IRepositoryWellRelated where TDto : AsbCloudApp.Data.IId, AsbCloudApp.Data.IWellRelated where TEntity : class, IId, IWellRelated { @@ -27,7 +27,7 @@ namespace AsbCloudInfrastructure.Repository var dtos = cache .Where(e => e.IdWell == idWell) - .ToList(); + .Select(Convert); return dtos; } @@ -41,7 +41,8 @@ namespace AsbCloudInfrastructure.Repository var dtos = cache .Where(e => idsWells.Contains(e.IdWell)) - .ToList(); + .Select(Convert); + return dtos; } } diff --git a/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs index b79277cc..eb59a94d 100644 --- a/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Repository { #nullable enable - public class CrudWellRelatedServiceBase : CrudServiceBase, IRepositoryWellRelated + public class CrudWellRelatedServiceBase : CrudRepositoryBase, IRepositoryWellRelated where TDto : AsbCloudApp.Data.IId, AsbCloudApp.Data.IWellRelated where TEntity : class, IId, IWellRelated { diff --git a/AsbCloudInfrastructure/Repository/FileRepository.cs b/AsbCloudInfrastructure/Repository/FileRepository.cs index deb43445..98d899cc 100644 --- a/AsbCloudInfrastructure/Repository/FileRepository.cs +++ b/AsbCloudInfrastructure/Repository/FileRepository.cs @@ -1,15 +1,10 @@ using AsbCloudApp.Data; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; -using AsbCloudApp.Services; using AsbCloudDb; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services; -using DocumentFormat.OpenXml.Drawing.Charts; -using DocumentFormat.OpenXml.Wordprocessing; using Mapster; using Microsoft.EntityFrameworkCore; -using Org.BouncyCastle.Asn1.Ocsp; using System; using System.Collections.Generic; using System.Linq; diff --git a/AsbCloudInfrastructure/Repository/QueryContainer.cs b/AsbCloudInfrastructure/Repository/QueryContainer.cs new file mode 100644 index 00000000..a1586220 --- /dev/null +++ b/AsbCloudInfrastructure/Repository/QueryContainer.cs @@ -0,0 +1,28 @@ +using AsbCloudDb.Model; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; + +namespace AsbCloudInfrastructure.Repository +{ + public class QueryContainer where TEntity : class, IId + { + protected readonly IAsbCloudDbContext dbContext; + protected readonly DbSet dbSet; + protected readonly Func> GetQuery; + + public QueryContainer(IAsbCloudDbContext context) + { + dbContext = context; + dbSet = context.Set(); + GetQuery = () => dbSet; + } + + public QueryContainer(IAsbCloudDbContext context, Func, IQueryable> makeQuery) + { + dbContext = context; + dbSet = context.Set(); + GetQuery = () => makeQuery(dbSet); + } + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Repository/SetpointsRequestRepository.cs b/AsbCloudInfrastructure/Repository/SetpointsRequestRepository.cs index c9379244..325cae07 100644 --- a/AsbCloudInfrastructure/Repository/SetpointsRequestRepository.cs +++ b/AsbCloudInfrastructure/Repository/SetpointsRequestRepository.cs @@ -4,6 +4,10 @@ using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Threading; +using System.Linq; namespace AsbCloudInfrastructure.Repository { @@ -18,6 +22,29 @@ namespace AsbCloudInfrastructure.Repository this.wellService = wellService; } + public virtual async Task UpdateRangeAsync(IEnumerable dtos, CancellationToken token) + { + var ids = dtos.Select(d => d.Id); + var existingEntities = await dbSet + .AsNoTracking() + .Where(d => ids.Contains(d.Id)) + .Select(d => d.Id) + .ToListAsync(token) + .ConfigureAwait(false); + + if (ids.Count() > existingEntities.Count) + return ICrudRepository.ErrorIdNotFound; + + foreach (var dto in dtos) + { + var entity = Convert(dto); + var entry = dbSet.Update(entity); + } + + var affected = await dbContext.SaveChangesAsync(token); + return affected; + } + protected override SetpointsRequestDto Convert(SetpointsRequest src) { var result = base.Convert(src); diff --git a/AsbCloudInfrastructure/Services/BackgroundWorkerService.cs b/AsbCloudInfrastructure/Services/BackgroundWorkerService.cs deleted file mode 100644 index ab4d723a..00000000 --- a/AsbCloudInfrastructure/Services/BackgroundWorkerService.cs +++ /dev/null @@ -1,193 +0,0 @@ -using AsbCloudApp.Services; -using Microsoft.Extensions.Configuration; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudInfrastructure.Services -{ - /// - /// Сервис выстраивает очередь из фоновых задач. Ограничивает количество одновременно выполняющихся задач. - /// - public class BackgroundWorkerService : IDisposable, IBackgroundWorkerService - { - private readonly Worker[] workers; - private readonly Dictionary works = new Dictionary(); - private bool isRunning = false; - private CancellationTokenSource cts; - private Task task; - - public BackgroundWorkerService(IConfiguration configuration) - { - var workersCount = configuration.GetValue("BackgroundWorkersCount", 4); - workers = new Worker[workersCount]; - for (int i = 0; i < workers.Length; i++) - workers[i] = new Worker(); - } - - ~BackgroundWorkerService() - { - Dispose(); - } - - public string Enqueue(Func func) - { - var work = new Work - { - ActionAsync = func - }; - return Enqueue(work); - } - - public string Enqueue(string id, Func func) - { - var work = new Work(id, func); - return Enqueue(work); - } - - public string Enqueue(string id, Func func, Func onError) - { - var work = new Work(id, func) - { - OnErrorAsync = onError - }; - return Enqueue(work); - } - - string Enqueue(Work work) - { - works[work.Id] = work; - if (!isRunning) - { - isRunning = true; - cts = new CancellationTokenSource(); - task = Task.Run(() => ExecuteAsync(cts.Token), cts.Token); - } - return work.Id; - } - - private Work Dequeue() - { - var item = works.First(); - works.Remove(item.Key); - return item.Value; - } - - public bool TryRemove(string id) - => works.Remove(id); - - public bool Contains(string id) - => works.ContainsKey(id); - - protected async Task ExecuteAsync(CancellationToken token) - { - while (works.Any() && !token.IsCancellationRequested) - { - var freeworker = workers.FirstOrDefault(w => !w.IsBusy); - if (freeworker is not null) - { - var work = Dequeue(); - freeworker.Start(work); - } - else - await Task.Delay(10, token).ConfigureAwait(false); - } - isRunning = false; - } - - public void Dispose() - { - cts?.Cancel(); - task?.Wait(1); - task?.Dispose(); - cts?.Dispose(); - task = null; - cts = null; - GC.SuppressFinalize(this); - } - } - - class Worker : IDisposable - { - private CancellationTokenSource cts; - private Task task; - public bool IsBusy { get; private set; } - - ~Worker() - { - Dispose(); - } - - public void Dispose() - { - Stop(); - GC.SuppressFinalize(this); - } - - public void Start(Work work) - { - IsBusy = true; - cts = new CancellationTokenSource(); - task = Task.Run(async () => - { - try - { - var actionTask = work.ActionAsync(work.Id, cts.Token); - await actionTask.WaitAsync(TimeSpan.FromMinutes(2), cts.Token); - } - catch (Exception ex) - { - Trace.TraceError(ex.Message); - - if (work.OnErrorAsync is not null) - { - try - { - await work.OnErrorAsync(work.Id, ex, cts.Token).ConfigureAwait(false); - } - catch (Exception exOnErrorHandler) - { - Trace.TraceError(exOnErrorHandler.Message); - } - } - } - finally - { - cts?.Dispose(); - cts = null; - IsBusy = false; - } - }, cts.Token); - } - - public void Stop() - { - cts?.Cancel(); - task?.Wait(1); - task = null; - cts?.Dispose(); - cts = null; - IsBusy = false; - } - } - class Work - { - public string Id { get; private set; } - public Func ActionAsync { get; set; } - public Func OnErrorAsync { get; set; } - - public Work() - { - Id = Guid.NewGuid().ToString(); - } - - public Work(string id, Func actionAsync) - { - Id = id; - ActionAsync = actionAsync; - } - } -} diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs index 87decac8..7cca4f97 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs @@ -268,7 +268,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations return query; } - private DetectedOperationDto Convert(DetectedOperation operation, WellDto well, IEnumerable operationValues, IEnumerable schedules) + private static DetectedOperationDto Convert(DetectedOperation operation, WellDto well, IEnumerable operationValues, IEnumerable schedules) { var dto = operation.Adapt(); dto.IdWell = well.Id; diff --git a/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionBackgroundService.cs b/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionWorkFactory.cs similarity index 69% rename from AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionBackgroundService.cs rename to AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionWorkFactory.cs index e4b7c450..8ff6ebb8 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionBackgroundService.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/OperationDetectionWorkFactory.cs @@ -1,7 +1,5 @@ using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; using System.Diagnostics; @@ -9,14 +7,16 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudInfrastructure.Services.DetectOperations.Detectors; +using AsbCloudInfrastructure.Background; +using Microsoft.Extensions.DependencyInjection; namespace AsbCloudInfrastructure.Services.DetectOperations { #nullable enable - public class OperationDetectionBackgroundService : BackgroundService + public static class OperationDetectionWorkFactory { - private readonly string connectionString; - private readonly TimeSpan period = TimeSpan.FromHours(1); + private const string workId = "Operation detection"; + private static readonly TimeSpan workPeriod = TimeSpan.FromMinutes(30); private static readonly DetectorAbstract[] detectors = new DetectorAbstract[] { @@ -31,49 +31,18 @@ namespace AsbCloudInfrastructure.Services.DetectOperations new DetectorTemplatingWhileDrilling(), }; - public OperationDetectionBackgroundService(IConfiguration configuration) - { - connectionString = configuration.GetConnectionString("DefaultConnection"); + public static WorkPeriodic MakeWork() + { + var workPeriodic = new WorkPeriodic(workId, WorkAction, workPeriod); + workPeriodic.Timeout = TimeSpan.FromMinutes(30); + return workPeriodic; } - protected override async Task ExecuteAsync(CancellationToken token = default) + // TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД. + private static async Task WorkAction(string _, IServiceProvider serviceProvider, CancellationToken token) { - var timeToStartAnalysis = DateTime.Now; - var options = new DbContextOptionsBuilder() - .UseNpgsql(connectionString) - .Options; + using var db = serviceProvider.GetRequiredService(); - while (!token.IsCancellationRequested) - { - if (DateTime.Now > timeToStartAnalysis) - { - timeToStartAnalysis = DateTime.Now + period; - try - { - using var context = new AsbCloudDbContext(options); - var added = await DetectedAllTelemetriesAsync(context, token); - Trace.TraceInformation($"Total detection complete. Added {added} operations."); - } - catch (Exception ex) - { - Trace.TraceError(ex.Message); - } - GC.Collect(); - } - - var ms = (int)(timeToStartAnalysis - DateTime.Now).TotalMilliseconds; - ms = ms > 100 ? ms : 100; - await Task.Delay(ms, token).ConfigureAwait(false); - } - } - - public override async Task StopAsync(CancellationToken token) - { - await base.StopAsync(token).ConfigureAwait(false); - } - - private static async Task DetectedAllTelemetriesAsync(IAsbCloudDbContext db, CancellationToken token) - { var lastDetectedDates = await db.DetectedOperations .GroupBy(o => o.IdTelemetry) .Select(g => new @@ -88,7 +57,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations .Select(t => t.Id) .ToListAsync(token); - var JounedlastDetectedDates = telemetryIds + var joinedlastDetectedDates = telemetryIds .GroupJoin(lastDetectedDates, t => t, o => o.IdTelemetry, @@ -97,8 +66,9 @@ namespace AsbCloudInfrastructure.Services.DetectOperations IdTelemetry = outer, inner.SingleOrDefault()?.LastDate, }); + var affected = 0; - foreach (var item in JounedlastDetectedDates) + foreach (var item in joinedlastDetectedDates) { var stopwatch = Stopwatch.StartNew(); var newOperations = await DetectOperationsAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token); @@ -109,7 +79,6 @@ namespace AsbCloudInfrastructure.Services.DetectOperations affected += await db.SaveChangesAsync(token); } } - return affected; } private static async Task> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) diff --git a/AsbCloudInfrastructure/Services/DrillParamsService.cs b/AsbCloudInfrastructure/Services/DrillParamsService.cs index a2ac61c6..7c5f8e62 100644 --- a/AsbCloudInfrastructure/Services/DrillParamsService.cs +++ b/AsbCloudInfrastructure/Services/DrillParamsService.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services { #nullable enable - public class DrillParamsService : CrudServiceBase, IDrillParamsService + public class DrillParamsService : CrudRepositoryBase, IDrillParamsService { private readonly IAsbCloudDbContext db; private readonly ITelemetryService telemetryService; diff --git a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs index b1a6d0c6..9ef92fd8 100644 --- a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs +++ b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs @@ -3,10 +3,11 @@ using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Repository; +using AsbCloudInfrastructure.Background; using Mapster; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.IO; @@ -16,6 +17,7 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.DrillingProgram { +# nullable enable public class DrillingProgramService : IDrillingProgramService { private static readonly Dictionary drillingProgramCreateErrors = new Dictionary(); @@ -25,9 +27,8 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram private readonly IUserRepository userRepository; private readonly IWellService wellService; private readonly IConfiguration configuration; - private readonly IBackgroundWorkerService backgroundWorker; + private readonly BackgroundWorker backgroundWorker; private readonly IEmailService emailService; - private readonly string connectionString; private const int idFileCategoryDrillingProgram = 1000; private const int idFileCategoryDrillingProgramPartsStart = 1001; @@ -55,7 +56,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram IUserRepository userRepository, IWellService wellService, IConfiguration configuration, - IBackgroundWorkerService backgroundWorker, + BackgroundWorker backgroundWorker, IEmailService emailService) { this.context = context; @@ -64,7 +65,6 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram this.wellService = wellService; this.configuration = configuration; this.backgroundWorker = backgroundWorker; - this.connectionString = configuration.GetConnectionString("DefaultConnection"); this.emailService = emailService; } @@ -127,7 +127,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram { Parts = parts, Program = files.FirstOrDefault(f => f.IdCategory == idFileCategoryDrillingProgram) - .Adapt(), + ?.Adapt(), PermissionToEdit = userRepository.HasPermission(idUser, "DrillingProgram.edit"), }; @@ -157,7 +157,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram else state.IdState = idStateNotInitialized; - await TryEnqueueMakeProgramAsync(idWell, state, token); + await EnqueueMakeProgramWorkAsync(idWell, state, token); return state; } @@ -299,7 +299,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram .AsNoTracking() .FirstOrDefaultAsync(p => p.IdWell == fileInfo.IdWell && p.IdFileCategory == fileInfo.IdCategory, token); - var user = part.RelatedUsers.FirstOrDefault(r => r.IdUser == idUser && r.IdUserRole == idUserRoleApprover)?.User; + var user = part?.RelatedUsers.FirstOrDefault(r => r.IdUser == idUser && r.IdUserRole == idUserRoleApprover)?.User; if (user is null) throw new ForbidException($"User {idUser} is not in the approvers list."); @@ -323,11 +323,11 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram else { // если все согласованты согласовали - оповещаем публикатора - var approvers = part.RelatedUsers + var approvers = part!.RelatedUsers .Where(u => u.IdUserRole == idUserRoleApprover); if (approvers .All(user => fileInfo.FileMarks - .Any(mark => (mark.IdMarkType == idMarkTypeApprove && mark.User.Id == user.IdUser && !mark.IsDeleted)) || + ?.Any(mark => (mark.IdMarkType == idMarkTypeApprove && mark.User.Id == user.IdUser && !mark.IsDeleted)) == true || (fileMarkDto.IdMarkType == idMarkTypeApprove && user.IdUser == idUser))) { await NotifyPublisherOnFullAccepAsync(fileMarkDto, token); @@ -359,7 +359,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram private async Task NotifyPublisherOnFullAccepAsync(FileMarkDto fileMark, CancellationToken token) { var file = await fileService.GetOrDefaultAsync(fileMark.IdFile, token); - var well = await wellService.GetOrDefaultAsync(file.IdWell, token); + var well = await wellService.GetOrDefaultAsync(file!.IdWell, token); var user = file.Author; var factory = new DrillingMailBodyFactory(configuration); var subject = factory.MakeSubject(well, "Загруженный вами документ полностью согласован"); @@ -371,7 +371,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram private async Task NotifyPublisherOnRejectAsync(FileMarkDto fileMark, CancellationToken token) { var file = await fileService.GetOrDefaultAsync(fileMark.IdFile, token); - var well = await wellService.GetOrDefaultAsync(file.IdWell, token); + var well = await wellService.GetOrDefaultAsync(file!.IdWell, token); var user = file.Author; var factory = new DrillingMailBodyFactory(configuration); var subject = factory.MakeSubject(well, "Загруженный вами документ отклонен"); @@ -405,12 +405,12 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram emailService.EnqueueSend(user.Email, subject, body); } - private DrillingProgramPartDto ConvertPart(int idUser, List fileCategories, List files, DrillingProgramPart partEntity, double timezoneOffset) + private static DrillingProgramPartDto ConvertPart(int idUser, List fileCategories, List files, DrillingProgramPart partEntity, double timezoneOffset) { var part = new DrillingProgramPartDto { IdFileCategory = partEntity.IdFileCategory, - Name = fileCategories.FirstOrDefault(c => c.Id == partEntity.IdFileCategory).Name, + Name = fileCategories.FirstOrDefault(c => c.Id == partEntity.IdFileCategory)!.Name, Approvers = partEntity.RelatedUsers .Where(r => r.IdUserRole == idUserRoleApprover) .Select(r => r.User.Adapt()), @@ -464,31 +464,27 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram return part; } - private async Task TryEnqueueMakeProgramAsync(int idWell, DrillingProgramStateDto state, CancellationToken token) + private async Task EnqueueMakeProgramWorkAsync(int idWell, DrillingProgramStateDto state, CancellationToken token) { if (state.IdState == idStateCreating) { var workId = MakeWorkId(idWell); if (!backgroundWorker.Contains(workId)) { - var well = await wellService.GetOrDefaultAsync(idWell, token); + var well = (await wellService.GetOrDefaultAsync(idWell, token))!; var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.xlsx"; var tempResultFilePath = Path.Combine(Path.GetTempPath(), "drillingProgram", resultFileName); - async Task funcProgramMake(string id, CancellationToken token) + + var workAction = async (string workId, IServiceProvider serviceProvider, CancellationToken token) => { - var contextOptions = new DbContextOptionsBuilder() - .UseNpgsql(connectionString) - .Options; - using var context = new AsbCloudDbContext(contextOptions); - var fileRepository = new FileRepository(context); - var fileStorageRepository = new FileStorageRepository(); - var fileService = new FileService(fileRepository, fileStorageRepository); + var context = serviceProvider.GetRequiredService(); + var fileService = serviceProvider.GetRequiredService(); var files = state.Parts.Select(p => fileService.GetUrl(p.File)); DrillingProgramMaker.UniteExcelFiles(files, tempResultFilePath, state.Parts, well); await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram, resultFileName, tempResultFilePath, token); - } + }; - Task funcOnErrorProgramMake(string workId, Exception exception, CancellationToken token) + var onErrorAction = (string workId, Exception exception, CancellationToken token) => { var message = $"Не удалось сформировать программу бурения по скважине {well?.Caption}"; drillingProgramCreateErrors[workId] = new() @@ -497,9 +493,15 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram Exception = exception.Message, }; return Task.CompletedTask; - } + }; - backgroundWorker.Enqueue(workId, funcProgramMake, funcOnErrorProgramMake); + var work = new WorkBase(workId, workAction) + { + ExecutionTime = TimeSpan.FromMinutes(1), + OnErrorAsync = onErrorAction + }; + + backgroundWorker.Push(work); } } } @@ -513,7 +515,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram private async Task RemoveDrillingProgramAsync(int idWell, CancellationToken token) { var workId = MakeWorkId(idWell); - backgroundWorker.TryRemove(workId); + backgroundWorker.Delete(workId); var filesIds = await context.Files .Where(f => f.IdWell == idWell && @@ -529,4 +531,5 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram private static string MakeWorkId(int idWell) => $"Make drilling program for wellId {idWell}"; } +#nullable disable } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Email/EmailService.cs b/AsbCloudInfrastructure/Services/Email/EmailService.cs index 84a1f73f..568d4098 100644 --- a/AsbCloudInfrastructure/Services/Email/EmailService.cs +++ b/AsbCloudInfrastructure/Services/Email/EmailService.cs @@ -8,26 +8,28 @@ using System.Linq; using System.Net.Mail; using System.Threading; using System.Threading.Tasks; +using AsbCloudInfrastructure.Background; namespace AsbCloudInfrastructure.Services { +#nullable enable public class EmailService : IEmailService { - private readonly IBackgroundWorkerService backgroundWorker; + private readonly BackgroundWorker backgroundWorker; private readonly bool IsConfigured; private readonly string sender; private readonly string smtpServer; private readonly string smtpPassword; - public EmailService(IBackgroundWorkerService backgroundWorker, IConfiguration configuration) + public EmailService(BackgroundWorker backgroundWorker, IConfiguration configuration) { - sender = configuration.GetValue("email:sender", null); - smtpPassword = configuration.GetValue("email:password", null); - smtpServer = configuration.GetValue("email:smtpServer", null); + sender = configuration.GetValue("email:sender", string.Empty); + smtpPassword = configuration.GetValue("email:password", string.Empty); + smtpServer = configuration.GetValue("email:smtpServer", string.Empty); - var configError = (string.IsNullOrEmpty(sender) || + var configError = string.IsNullOrEmpty(sender) || string.IsNullOrEmpty(smtpPassword) || - string.IsNullOrEmpty(smtpServer)); + string.IsNullOrEmpty(smtpServer); IsConfigured = !configError; @@ -44,20 +46,21 @@ namespace AsbCloudInfrastructure.Services Trace.TraceWarning("smtp is not configured"); return; } - var jobId = CalcJobId(addresses, subject, htmlBody); - if (!backgroundWorker.Contains(jobId)) + var workId = MakeWorkId(addresses, subject, htmlBody); + if (!backgroundWorker.Contains(workId)) { - var action = MakeEmailSendJobAsync(addresses, subject, htmlBody); - backgroundWorker.Enqueue(jobId, action); + var workAction = MakeEmailSendWorkAction(addresses, subject, htmlBody); + var work = new WorkBase(workId, workAction); + backgroundWorker.Push(work); } } - private Func MakeEmailSendJobAsync(IEnumerable addresses, string subject, string htmlBody) + private Func MakeEmailSendWorkAction(IEnumerable addresses, string subject, string htmlBody) { var mailAddresses = new List(); foreach (var address in addresses) { - if (MailAddress.TryCreate(address, out MailAddress mailAddress)) + if (MailAddress.TryCreate(address, out MailAddress? mailAddress)) mailAddresses.Add(mailAddress); else Trace.TraceWarning($"Mail {address} is not correct."); @@ -69,16 +72,16 @@ namespace AsbCloudInfrastructure.Services if (string.IsNullOrEmpty(subject)) throw new ArgumentInvalidException($"{nameof(subject)} should be set", nameof(subject)); - var func = async (string id, CancellationToken token) => + var workAction = async (string id, IServiceProvider serviceProvider, CancellationToken token) => { var from = new MailAddress(sender); - - var message = new MailMessage(); - message.From = from; + var message = new MailMessage + { + From = from + }; foreach (var mailAddress in mailAddresses) message.To.Add(mailAddress); - //message.To.Add("support@digitaldrilling.ru"); message.BodyEncoding = System.Text.Encoding.UTF8; message.Body = htmlBody; @@ -91,12 +94,12 @@ namespace AsbCloudInfrastructure.Services client.Credentials = new System.Net.NetworkCredential(sender, smtpPassword); await client.SendMailAsync(message, token); - Trace.TraceInformation($"Send email to {string.Join(',', addresses)} subj:{subject} html body count {htmlBody.Count()}"); + Trace.TraceInformation($"Send email to {string.Join(',', addresses)} subj:{subject} html body count {htmlBody.Length}"); }; - return func; + return workAction; } - private string CalcJobId(IEnumerable addresses, string subject, string content) + private static string MakeWorkId(IEnumerable addresses, string subject, string content) { var hash = GetHashCode(addresses); hash ^= subject.GetHashCode(); @@ -114,4 +117,5 @@ namespace AsbCloudInfrastructure.Services return hash; } } +#nullable disable } diff --git a/AsbCloudInfrastructure/Services/FileCategoryService.cs b/AsbCloudInfrastructure/Services/FileCategoryService.cs index 8721ef25..b50c9418 100644 --- a/AsbCloudInfrastructure/Services/FileCategoryService.cs +++ b/AsbCloudInfrastructure/Services/FileCategoryService.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services { - public class FileCategoryService : CrudCacheServiceBase, IFileCategoryService + public class FileCategoryService : CrudCacheRepositoryBase, IFileCategoryService { public FileCategoryService(IAsbCloudDbContext context, IMemoryCache memoryCache) : base(context, memoryCache) { } @@ -22,7 +22,8 @@ namespace AsbCloudInfrastructure.Services .ConfigureAwait(false); var dtos = cache .Where(f => f.Id >= 10000) - .Where(f => f.Id <= 20000); + .Where(f => f.Id <= 20000) + .Select(Convert); return dtos; } diff --git a/AsbCloudInfrastructure/Services/LimitingParameterBackgroundService.cs b/AsbCloudInfrastructure/Services/LimitingParameterBackgroundService.cs index 261ceb9c..1420ea89 100644 --- a/AsbCloudInfrastructure/Services/LimitingParameterBackgroundService.cs +++ b/AsbCloudInfrastructure/Services/LimitingParameterBackgroundService.cs @@ -1,61 +1,37 @@ using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; using System; using System.Data.Common; using System.Data; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; +using AsbCloudInfrastructure.Background; +using Microsoft.Extensions.DependencyInjection; namespace AsbCloudInfrastructure.Services { #nullable enable - internal class LimitingParameterBackgroundService : BackgroundService + internal static class LimitingParameterCalcWorkFactory { - private readonly string connectionString; - private readonly TimeSpan period = TimeSpan.FromHours(1); + private const string workId = "Limiting parameter calc"; + private static readonly TimeSpan workPeriod = TimeSpan.FromMinutes(30); - public LimitingParameterBackgroundService(IConfiguration configuration) + public static WorkPeriodic MakeWork() { - connectionString = configuration.GetConnectionString("DefaultConnection"); - } - - protected override async Task ExecuteAsync(CancellationToken token) - { - var timeToStart = DateTime.Now; - var options = new DbContextOptionsBuilder() - .UseNpgsql(connectionString) - .Options; - while (!token.IsCancellationRequested) + var workPeriodic = new WorkPeriodic(workId, WorkAction, workPeriod) { - if (DateTime.Now > timeToStart) - { - timeToStart = DateTime.Now + period; - try - { - using var context = new AsbCloudDbContext(options); - var added = await LimitingParameterAsync(context, token); - Trace.TraceInformation($"Total limiting parameter complete. Added {added} limiting parameters."); - } - catch (Exception ex) - { - Trace.TraceError(ex.Message); - } - GC.Collect(); - } - var ms = (int)(timeToStart - DateTime.Now).TotalMilliseconds; - ms = ms > 100 ? ms : 100; - await Task.Delay(ms, token).ConfigureAwait(false); - } + Timeout = TimeSpan.FromMinutes(30) + }; + return workPeriodic; } - private static async Task LimitingParameterAsync(IAsbCloudDbContext context, CancellationToken token) + // TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД. + private static async Task WorkAction(string _, IServiceProvider serviceProvider, CancellationToken token) { - var lastDetectedDates = await context.LimitingParameter + using var db = serviceProvider.GetRequiredService(); + var lastDetectedDates = await db.LimitingParameter .GroupBy(o => o.IdTelemetry) .Select(g => new { @@ -64,7 +40,7 @@ namespace AsbCloudInfrastructure.Services }) .ToListAsync(token); - var telemetryIds = await context.Telemetries + var telemetryIds = await db.Telemetries .Where(t => t.Info != null && t.TimeZone != null) .Select(t => t.Id) .ToListAsync(token); @@ -79,17 +55,15 @@ namespace AsbCloudInfrastructure.Services inner.SingleOrDefault()?.LastDate, }); - var affected = 0; foreach (var item in telemetryLastDetectedDates) { - var newLimitingParameters = await GetLimitingParameterAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, context, token); + var newLimitingParameters = await GetLimitingParameterAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token); if (newLimitingParameters?.Any() == true) { - context.LimitingParameter.AddRange(newLimitingParameters); - affected += await context.SaveChangesAsync(token); + db.LimitingParameter.AddRange(newLimitingParameters); + await db.SaveChangesAsync(token); } } - return affected; } private static async Task> GetLimitingParameterAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) diff --git a/AsbCloudInfrastructure/Services/LimitingParameterService.cs b/AsbCloudInfrastructure/Services/LimitingParameterService.cs index c50729f8..0a609507 100644 --- a/AsbCloudInfrastructure/Services/LimitingParameterService.cs +++ b/AsbCloudInfrastructure/Services/LimitingParameterService.cs @@ -15,7 +15,7 @@ namespace AsbCloudInfrastructure.Services { private readonly ILimitingParameterRepository limitingParameterRepository; private readonly IWellService wellService; - private readonly Dictionary feedRegulatorData = new Dictionary() + private readonly Dictionary feedRegulatorData = new () { { 0, "Нет ограничения" }, { 1, "МСП" }, @@ -63,6 +63,11 @@ namespace AsbCloudInfrastructure.Services return result; } + public Dictionary GetLimitingParameteraNames() //TODO: Перенести получение ограничений в репозиторий + { + return feedRegulatorData; + } + private IEnumerable TrimLimitingParameters(IEnumerable data, LimitingParameterRequest request) { var result = data.Select((x) => diff --git a/AsbCloudInfrastructure/Services/ReportService.cs b/AsbCloudInfrastructure/Services/ReportService.cs index 7143e063..c46d4682 100644 --- a/AsbCloudInfrastructure/Services/ReportService.cs +++ b/AsbCloudInfrastructure/Services/ReportService.cs @@ -1,11 +1,11 @@ using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Repository; +using AsbCloudInfrastructure.Background; using AsbSaubReport; using Mapster; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.IO; @@ -15,30 +15,32 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services { +#nullable enable public class ReportService : IReportService { private readonly IAsbCloudDbContext db; - private readonly string connectionString; private readonly ITelemetryService telemetryService; private readonly IWellService wellService; - private readonly IBackgroundWorkerService backgroundWorkerService; - - public ReportService(IAsbCloudDbContext db, IConfiguration configuration, - ITelemetryService telemetryService, IWellService wellService, IBackgroundWorkerService backgroundWorkerService) - { - this.db = db; - this.connectionString = configuration.GetConnectionString("DefaultConnection"); - this.wellService = wellService; - this.backgroundWorkerService = backgroundWorkerService; - this.telemetryService = telemetryService; - ReportCategoryId = db.FileCategories.AsNoTracking() - .FirstOrDefault(c => - c.Name.Equals("Рапорт")).Id; - } + private readonly BackgroundWorker backgroundWorkerService; public int ReportCategoryId { get; private set; } - public string CreateReport(int idWell, int idUser, int stepSeconds, int format, DateTime begin, + public ReportService(IAsbCloudDbContext db, + ITelemetryService telemetryService, + IWellService wellService, + BackgroundWorker backgroundWorkerService) + { + this.db = db; + this.wellService = wellService; + this.backgroundWorkerService = backgroundWorkerService; + this.telemetryService = telemetryService; + ReportCategoryId = db.FileCategories + .AsNoTracking() + .First(c => c.Name.Equals("Рапорт")) + .Id; + } + + public string EnqueueCreateReportWork(int idWell, int idUser, int stepSeconds, int format, DateTime begin, DateTime end, Action progressHandler) { var timezoneOffset = wellService.GetTimezone(idWell).Hours; @@ -47,12 +49,12 @@ namespace AsbCloudInfrastructure.Services var beginRemote = begin.ToTimeZoneOffsetHours(timezoneOffset); var endRemote = end.ToTimeZoneOffsetHours(timezoneOffset); - var newReportId = backgroundWorkerService.Enqueue(async (id, token) => + var workId = $"create report by wellid:{idWell} for userid:{idUser} requested at {DateTime.Now}"; + + var workAction = async (string id, IServiceProvider serviceProvider, CancellationToken token) => { - var contextOptions = new DbContextOptionsBuilder() - .UseNpgsql(connectionString) - .Options; - using var context = new AsbCloudDbContext(contextOptions); + using var context = serviceProvider.GetRequiredService(); + var fileService = serviceProvider.GetRequiredService(); var tempDir = Path.Combine(Path.GetTempPath(), "report"); @@ -65,11 +67,8 @@ namespace AsbCloudInfrastructure.Services progressHandler.Invoke(e.Adapt(), id); }; generator.Make(reportFileName); - - var fileRepository = new FileRepository(context); - var fileStorageRepository = new FileStorageRepository(); - var fileService = new FileService(fileRepository, fileStorageRepository); - var fileInfo = await fileService.MoveAsync(idWell, idUser, ReportCategoryId, reportFileName, reportFileName, token); + + var fileInfo = (await fileService.MoveAsync(idWell, idUser, ReportCategoryId, reportFileName, reportFileName, token))!; progressHandler.Invoke(new { @@ -91,13 +90,17 @@ namespace AsbCloudInfrastructure.Services }; context.ReportProperties.Add(newReportProperties); context.SaveChanges(); - }); + }; + + var work = new WorkBase(workId, workAction); + backgroundWorkerService.Push(work); + progressHandler.Invoke(new ReportProgressDto { Operation = "Ожидает начала в очереди.", Progress = 0f, - }, newReportId); - return newReportId; + }, workId); + return workId; } public int GetReportPagesCount(int idWell, DateTime begin, DateTime end, int stepSeconds, int format) @@ -106,12 +109,12 @@ namespace AsbCloudInfrastructure.Services var beginRemote = begin.ToTimeZoneOffsetHours(timezoneOffset); var endRemote = end.ToTimeZoneOffsetHours(timezoneOffset); - var generator = GetReportGenerator(idWell, beginRemote, endRemote, stepSeconds, format, (AsbCloudDbContext)db); + var generator = GetReportGenerator(idWell, beginRemote, endRemote, stepSeconds, format, db); var pagesCount = generator.GetPagesCount(); return pagesCount; } - public DatesRangeDto GetDatesRangeOrDefault(int idWell) + public DatesRangeDto? GetDatesRangeOrDefault(int idWell) { var idTelemetry = telemetryService.GetOrDefaultIdTelemetryByIdWell(idWell); if (idTelemetry is null) @@ -128,8 +131,8 @@ namespace AsbCloudInfrastructure.Services .OrderBy(o => o.File.UploadDate) .AsNoTracking() .Take(1024); - var properties = await propertiesQuery.ToListAsync(token); - return properties.Select(p => new ReportPropertiesDto + var entities = await propertiesQuery.ToListAsync(token); + var dtos = entities.Select(p => new ReportPropertiesDto { Id = p.Id, Name = p.File.Name, @@ -151,10 +154,11 @@ namespace AsbCloudInfrastructure.Services Step = p.Step, Format = p.Format == 0 ? ".pdf" : ".las" }); + return dtos; } private static IReportGenerator GetReportGenerator(int idWell, DateTime begin, - DateTime end, int stepSeconds, int format, AsbCloudDbContext context) + DateTime end, int stepSeconds, int format, IAsbCloudDbContext context) { var dataSource = new ReportDataSourcePgCloud(context, idWell); IReportGenerator generator = format switch @@ -173,4 +177,5 @@ namespace AsbCloudInfrastructure.Services return generator; } } +#nullable disable } diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs index af2d471a..00c583c1 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs @@ -14,7 +14,7 @@ namespace AsbCloudInfrastructure.Services.SAUB public class TelemetryDataCache where TDto : AsbCloudApp.Data.ITelemetryData { - private const int activeWellCapacity = 24 * 60 * 60; + private const int activeWellCapacity = 12 * 60 * 60; private const int doneWellCapacity = 65 * 60; private readonly ConcurrentDictionary> caches; diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs index 190094a1..d3f80a9b 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs @@ -41,6 +41,7 @@ namespace AsbCloudInfrastructure.Services.SAUB public TelemetryTracker(IConfiguration configuration, IMemoryCache memoryCache) { + // TODO: make this background work var contextOptions = new DbContextOptionsBuilder() .UseNpgsql(configuration.GetConnectionString("DefaultConnection")) .Options; diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeBackgroundService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeCalcWorkFactory.cs similarity index 86% rename from AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeBackgroundService.cs rename to AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeCalcWorkFactory.cs index 7f241ca3..9efc29a8 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeBackgroundService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeCalcWorkFactory.cs @@ -1,9 +1,9 @@ using AsbCloudDb.Model; using AsbCloudDb.Model.Subsystems; +using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Services.Subsystems.Utils; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Data; @@ -16,56 +16,30 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.Subsystems { #nullable enable - internal class SubsystemOperationTimeBackgroundService : BackgroundService + internal static class SubsystemOperationTimeCalcWorkFactory { - private readonly string connectionString; - private readonly TimeSpan period = TimeSpan.FromHours(1); + private const string workId = "Subsystem operation time calc"; + private static readonly TimeSpan workPeriod = TimeSpan.FromMinutes(30); + private const int idSubsytemTorqueMaster = 65537; private const int idSubsytemSpinMaster = 65536; private const int idSubsytemAkb = 1; private const int idSubsytemMse = 2; - public SubsystemOperationTimeBackgroundService(IConfiguration configuration) + public static WorkPeriodic MakeWork() { - connectionString = configuration.GetConnectionString("DefaultConnection"); - } - - protected override async Task ExecuteAsync(CancellationToken token) - { - var timeToStart = DateTime.Now; - var options = new DbContextOptionsBuilder() - .UseNpgsql(connectionString) - .Options; - while (!token.IsCancellationRequested) + var workPeriodic = new WorkPeriodic(workId, WorkAction, workPeriod) { - if (DateTime.Now > timeToStart) - { - timeToStart = DateTime.Now + period; - try - { - using var context = new AsbCloudDbContext(options); - var added = await OperationTimeAllTelemetriesAsync(context, token); - Trace.TraceInformation($"Total subsystem operation time complete. Added {added} operations time."); - } - catch (Exception ex) - { - Trace.TraceError(ex.Message); - } - GC.Collect(); - } - var ms = (int)(timeToStart - DateTime.Now).TotalMilliseconds; - ms = ms > 100 ? ms : 100; - await Task.Delay(ms, token).ConfigureAwait(false); - } + Timeout = TimeSpan.FromMinutes(30) + }; + return workPeriodic; } - public override async Task StopAsync(CancellationToken token) + // TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД. + private static async Task WorkAction(string _, IServiceProvider serviceProvider, CancellationToken token) { - await base.StopAsync(token).ConfigureAwait(false); - } + using var db = serviceProvider.GetRequiredService(); - private static async Task OperationTimeAllTelemetriesAsync(IAsbCloudDbContext db, CancellationToken token) - { var lastDetectedDates = await db.SubsystemOperationTimes .GroupBy(o => o.IdTelemetry) .Select(g => new @@ -90,23 +64,21 @@ namespace AsbCloudInfrastructure.Services.Subsystems inner.SingleOrDefault()?.LastDate, }); - var affected = 0; foreach (var item in telemetryLastDetectedDates) { var newOperationsSaub = await OperationTimeSaubAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token); if (newOperationsSaub?.Any() == true) { db.SubsystemOperationTimes.AddRange(newOperationsSaub); - affected += await db.SaveChangesAsync(token); + await db.SaveChangesAsync(token); } var newOperationsSpin = await OperationTimeSpinAsync(item.IdTelemetry, item.LastDate ?? DateTimeOffset.MinValue, db, token); if (newOperationsSpin?.Any() == true) { db.SubsystemOperationTimes.AddRange(newOperationsSpin); - affected += await db.SaveChangesAsync(token); + await db.SaveChangesAsync(token); } } - return affected; } private static async Task ExecuteReaderAsync(IAsbCloudDbContext db, string query, CancellationToken token) diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs index ada2ab42..16a48b56 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs @@ -23,13 +23,13 @@ namespace AsbCloudInfrastructure.Services.Subsystems private readonly IAsbCloudDbContext db; private readonly IWellService wellService; - private readonly ICrudService subsystemService; + private readonly ICrudRepository subsystemService; private readonly IDetectedOperationService detectedOperationService; public const int IdSubsystemAKB = 1; public const int IdSubsystemMSE = 2; public const int IdSubsystemSpin = 65536; public const int IdSubsystemTorque = 65537; - public SubsystemOperationTimeService(IAsbCloudDbContext db, IWellService wellService, ICrudService subsystemService, IDetectedOperationService detectedOperationService) + public SubsystemOperationTimeService(IAsbCloudDbContext db, IWellService wellService, ICrudRepository subsystemService, IDetectedOperationService detectedOperationService) { this.db = db; this.wellService = wellService; diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs index 6491265a..e73e6d84 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs @@ -15,7 +15,7 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.Subsystems { #nullable enable - internal class SubsystemService : CrudCacheServiceBase, ISubsystemService + internal class SubsystemService : CrudCacheRepositoryBase, ISubsystemService { private readonly IWellService wellService; public SubsystemService(IAsbCloudDbContext dbContext, IMemoryCache memoryCache, IWellService wellService) : base(dbContext, memoryCache) diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index c96672f7..3d73dbe4 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -15,13 +15,13 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services { - public class WellService : CrudCacheServiceBase, IWellService + public class WellService : CrudCacheRepositoryBase, IWellService { private const string relationCompaniesWellsCacheTag = "RelationCompaniesWells"; private static readonly TimeSpan relationCompaniesWellsCacheObsolence = TimeSpan.FromMinutes(15); private readonly ITelemetryService telemetryService; - private readonly ICrudService companyTypesService; + private readonly ICrudRepository companyTypesService; private readonly ITimezoneService timezoneService; private readonly IWellOperationService wellOperationService; @@ -43,7 +43,7 @@ namespace AsbCloudInfrastructure.Services this.timezoneService = timezoneService; this.wellOperationService = new WellOperationService.WellOperationService(db, memoryCache, this); - companyTypesService = new CrudCacheServiceBase(dbContext, memoryCache); + companyTypesService = new CrudCacheRepositoryBase(dbContext, memoryCache); } private IEnumerable GetCacheRelationCompanyWell() @@ -81,9 +81,10 @@ namespace AsbCloudInfrastructure.Services .Select(r => r.IdWell); var wellsDtos = (await GetCacheAsync(token)) - .Where(w => wellsIds.Contains(w.Id)); + .Where(w => wellsIds.Contains(w.Id)) + .Select(Convert); - return wellsDtos.ToList(); + return wellsDtos; } public override async Task InsertAsync(WellDto dto, CancellationToken token = default) @@ -154,9 +155,8 @@ namespace AsbCloudInfrastructure.Services public async Task GetWellCaptionByIdAsync(int idWell, CancellationToken token) { - var entity = await GetOrDefaultAsync(idWell, token).ConfigureAwait(false); - var dto = Convert(entity); - return dto.Caption; + var entity = await GetOrDefaultAsync(idWell, token).ConfigureAwait(false); + return entity!.Caption; } public async Task> GetCompaniesAsync(int idWell, CancellationToken token) diff --git a/AsbCloudInfrastructure/Startup.cs b/AsbCloudInfrastructure/Startup.cs index 5ccb0df7..36a85cc1 100644 --- a/AsbCloudInfrastructure/Startup.cs +++ b/AsbCloudInfrastructure/Startup.cs @@ -1,26 +1,72 @@ using AsbCloudApp.Services; using AsbCloudDb.Model; +using AsbCloudInfrastructure.Services.DetectOperations; +using AsbCloudInfrastructure.Services.Subsystems; +using AsbCloudInfrastructure.Services; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; -using System.Linq; +using System.Threading.Tasks; +using System.Threading; +using AsbCloudInfrastructure.Background; namespace AsbCloudInfrastructure { public class Startup { - public static void BeforeRunHandler(IHost host, IConfigurationRoot configuration) + public static void BeforeRunHandler(IHost host) { using var scope = host.Services.CreateScope(); - var context = scope.ServiceProvider.GetService(); - context.Database.SetCommandTimeout(TimeSpan.FromSeconds(2 * 60)); + var provider = scope.ServiceProvider; + var context = provider.GetService(); + context.Database.SetCommandTimeout(TimeSpan.FromSeconds(2 * 60)); context.Database.Migrate(); - var wellService = scope.ServiceProvider.GetService(); - wellService.EnshureTimezonesIsSetAsync(System.Threading.CancellationToken.None).Wait(); + var wellService = provider.GetRequiredService(); + wellService.EnshureTimezonesIsSetAsync(CancellationToken.None).Wait();// TODO: make this background work + + var backgroundWorker = provider.GetRequiredService(); + backgroundWorker.Push(OperationDetectionWorkFactory.MakeWork()); + backgroundWorker.Push(SubsystemOperationTimeCalcWorkFactory.MakeWork()); + backgroundWorker.Push(LimitingParameterCalcWorkFactory.MakeWork()); + backgroundWorker.Push(MakeMemoryMonitoringWork()); + + Task.Delay(1_000) + .ContinueWith(async (_) => await backgroundWorker.StartAsync(CancellationToken.None)); + } + + static WorkPeriodic MakeMemoryMonitoringWork() + { + var workId = "Memory monitoring"; + var workAction = (string _, IServiceProvider _, CancellationToken _) => { + var bytes = GC.GetTotalMemory(false); + var bytesString = FromatBytes(bytes); + System.Diagnostics.Trace.TraceInformation($"Total memory allocated is {bytesString} bytes. DbContext count is:{AsbCloudDb.Model.AsbCloudDbContext.ReferenceCount}"); + return Task.CompletedTask; + }; + var workPeriod = TimeSpan.FromMinutes(1); + var work = new WorkPeriodic(workId, workAction, workPeriod); + return work; + } + + static string FromatBytes(long bytes) + { + const double gigaByte = 1024 * 1024 * 1024; + const double megaByte = 1024 * 1024; + const double kiloByte = 1024; + + if (bytes > 10 * gigaByte) + return (bytes / gigaByte).ToString("### ### ###.## Gb"); + + if (bytes > 10 * megaByte) + return (bytes / megaByte).ToString("### ### ###.## Mb"); + + if (bytes > 10 * kiloByte) + return (bytes / megaByte).ToString("### ### ###.## Kb"); + + return bytes.ToString("### ### ###"); } } } diff --git a/AsbCloudWebApi.Tests/RepositoryFactory.cs b/AsbCloudWebApi.Tests/RepositoryFactory.cs index a8588dda..984fb494 100644 --- a/AsbCloudWebApi.Tests/RepositoryFactory.cs +++ b/AsbCloudWebApi.Tests/RepositoryFactory.cs @@ -11,7 +11,7 @@ namespace AsbCloudWebApi.Tests { public static Mock Make(ICollection data) where TDto : AsbCloudApp.Data.IId - where TRepository : class, ICrudService + where TRepository : class, ICrudRepository { var repositoryMock = new Mock(); diff --git a/AsbCloudWebApi.Tests/ServicesTests/BackgroundWorkerServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/BackgroundWorkerServiceTest.cs new file mode 100644 index 00000000..133f6b36 --- /dev/null +++ b/AsbCloudWebApi.Tests/ServicesTests/BackgroundWorkerServiceTest.cs @@ -0,0 +1,244 @@ +using Microsoft.Extensions.DependencyInjection; +using Moq; +using System; +using AsbCloudInfrastructure.Background; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace AsbCloudWebApi.Tests.ServicesTests +{ + public class BackgroundWorkerTest + { + private readonly Mock mockServiceProvider; + private readonly Mock mockServiceScopeFactory; + private readonly Func someAction = (string id, IServiceProvider scope, CancellationToken token) => Task.CompletedTask; + + public BackgroundWorkerTest() + { + var mockServiceScope = new Mock(); + mockServiceScopeFactory = new Mock(); + mockServiceProvider = new Mock(); + + mockServiceScope.SetReturnsDefault(mockServiceProvider.Object); + mockServiceProvider.SetReturnsDefault(mockServiceScopeFactory.Object); + mockServiceProvider.Setup(s => s.GetService(It.IsAny())) + .Returns(mockServiceScopeFactory.Object); + mockServiceScopeFactory.SetReturnsDefault(mockServiceScope.Object); + } + + [Fact] + public void Contains_returns_true() + { + mockServiceScopeFactory.Invocations.Clear(); + + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + const string work1Id = "long name 1"; + const string work2Id = "long name 2"; + + var work1 = new WorkBase(work1Id, someAction); + var work2 = new WorkPeriodic(work2Id, someAction, TimeSpan.Zero); + + BackgroundWorker.Push(work1); + BackgroundWorker.Push(work2); + + Assert.True(BackgroundWorker.Contains(work1Id)); + Assert.True(BackgroundWorker.Contains(work2Id)); + Assert.False(BackgroundWorker.Contains(work2Id + work1Id)); + Assert.False(BackgroundWorker.Contains(string.Empty)); + } + + [Fact] + public async Task Push_makes_new_scope_after_start() + { + mockServiceScopeFactory.Invocations.Clear(); + + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + var work = new WorkBase("", someAction); + BackgroundWorker.Push(work); + await BackgroundWorker.StartAsync(CancellationToken.None); + await Task.Delay(10); + + mockServiceScopeFactory.Verify(f => f.CreateScope()); + } + + [Fact] + public async Task Makes_primary_work_done() + { + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + var workDone = false; + var work = new WorkBase("", (_, _, _) => + { + workDone = true; + return Task.CompletedTask; + }); + BackgroundWorker.Push(work); + await BackgroundWorker.StartAsync(CancellationToken.None); + await Task.Delay(10); + + Assert.True(workDone); + } + + [Fact] + public async Task Sets_ExecutionTime_after_work_done() + { + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + var work = new WorkBase("", someAction); + BackgroundWorker.Push(work); + await BackgroundWorker.StartAsync(CancellationToken.None); + await Task.Delay(10); + + Assert.True(work.ExecutionTime > TimeSpan.Zero); + } + + [Fact] + public async Task Makes_periodic_work_done() + { + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + var workDone = false; + var work = new WorkPeriodic("", (_, _, _) => + { + workDone = true; + return Task.CompletedTask; + }, + TimeSpan.FromMilliseconds(10)); + BackgroundWorker.Push(work); + await BackgroundWorker.StartAsync(CancellationToken.None); + await Task.Delay(20); + + Assert.True(workDone); + } + + [Fact] + public async Task Does_not_start_periodic_work() + { + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + var workDone = false; + var work = new WorkPeriodic("", (_, _, _) => + { + workDone = true; + return Task.CompletedTask; + }, + TimeSpan.FromSeconds(30)) + { + LastStart = DateTime.Now + }; + BackgroundWorker.Push(work); + + await BackgroundWorker.StartAsync(CancellationToken.None); + await Task.Delay(20); + + Assert.False(workDone); + } + + [Fact] + public async Task Follows_work_priority() + { + var order = 0; + var work1Order = -1; + var work2Order = -1; + + var work1 = new WorkPeriodic("1", (_, _, _) => + { + work1Order = order++; + return Task.CompletedTask; + }, + TimeSpan.FromMilliseconds(1) + ); + + var work2 = new WorkBase("2", (_, _, _) => + { + work2Order = order++; + return Task.CompletedTask; + }); + + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + BackgroundWorker.Push(work2); + BackgroundWorker.Push(work1); + + await BackgroundWorker.StartAsync(CancellationToken.None); + await Task.Delay(2_100); + + Assert.True(work2Order < work1Order); + } + + [Fact] + public async Task Runs_second_after_delete_first() + { + var workDone = false; + + var work1 = new WorkBase("1", someAction); + var work2 = new WorkPeriodic("2", (_, _, _) => + { + workDone = true; + return Task.CompletedTask; + }, TimeSpan.FromMilliseconds(1)); + + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + BackgroundWorker.Push(work1); + BackgroundWorker.Push(work2); + BackgroundWorker.Delete("1"); + + await BackgroundWorker.StartAsync(CancellationToken.None); + await Task.Delay(10); + + Assert.True(workDone); + } + + [Fact] + public async Task Aborts_long_work() + { + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + var workCanceled = false; + var work = new WorkBase("", async (_, _, token) => await Task.Delay(1000000, token)) + { + Timeout = TimeSpan.FromMilliseconds(1), + OnErrorAsync = async (id, ex, token) => + { + workCanceled = ex is System.TimeoutException; + await Task.CompletedTask; + } + }; + + BackgroundWorker.Push(work); + await BackgroundWorker.StartAsync(CancellationToken.None); + await Task.Delay(20 * 4); + + Assert.True(workCanceled); + } + + [Fact] + public async Task Execution_continues_after_work_exception() + { + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + var work2done = false; + var work1 = new WorkBase("1", (_, _, _) => throw new Exception()); + var work2 = new WorkBase("2", (_, _, _) => + { + work2done = true; + return Task.CompletedTask; + }); + + BackgroundWorker.Push(work1); + BackgroundWorker.Push(work2); + + await BackgroundWorker.StartAsync(CancellationToken.None); + await Task.Delay(2_100); + + Assert.True(work2done); + } + + [Fact] + public void Push_not_unique_id_should_throw() + { + var work1 = new WorkPeriodic("1", someAction, TimeSpan.FromSeconds(30)); + var work2 = new WorkBase("1", someAction); + + var BackgroundWorker = new BackgroundWorker(mockServiceProvider.Object); + BackgroundWorker.Push(work1); + + Assert.Throws( + () => BackgroundWorker.Push(work2)); + } + } +} diff --git a/AsbCloudWebApi.Tests/ServicesTests/CrudServiceTestAbstract.cs b/AsbCloudWebApi.Tests/ServicesTests/CrudServiceTestAbstract.cs index d427e038..4b9a6ebb 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/CrudServiceTestAbstract.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/CrudServiceTestAbstract.cs @@ -9,7 +9,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests public abstract class CrudServiceTestAbstract where TDto : AsbCloudApp.Data.IId { - private readonly ICrudService service; + private readonly ICrudRepository service; public CrudServiceTestAbstract() { @@ -17,7 +17,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests service = MakeService(); } - protected abstract ICrudService MakeService(); + protected abstract ICrudRepository MakeService(); protected abstract TDto MakeNewItem(); [Fact] diff --git a/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs index e95c7cc3..f907f889 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs @@ -19,10 +19,10 @@ namespace AsbCloudWebApi.Tests.ServicesTests return item; } - protected override ICrudService MakeService() + protected override ICrudRepository MakeService() { var dbContext = TestHelpter.MakeRealTestContext(); - return new CrudCacheServiceBase(dbContext, TestHelpter.MemoryCache); + return new CrudCacheRepositoryBase(dbContext, TestHelpter.MemoryCache); } } } diff --git a/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs index f829c76c..b0f0e518 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs @@ -27,11 +27,11 @@ namespace AsbCloudWebApi.Tests.ServicesTests Surname = "Тестович" } }; - private ICrudService service; + private ICrudRepository service; public DrillerServiceTest() { - var repositoryMock = RepositoryFactory.Make, DrillerDto>(Drillers); + var repositoryMock = RepositoryFactory.Make, DrillerDto>(Drillers); repositoryMock.Setup(x => x.GetAllAsync(It.IsAny())) .Returns(() => { diff --git a/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs index e823189b..05833333 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs @@ -2,7 +2,7 @@ using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Repository; +using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Services.DrillingProgram; using Mapster; using Microsoft.Extensions.Configuration; @@ -83,8 +83,8 @@ namespace AsbCloudWebApi.Tests.ServicesTests private readonly Mock userRepositoryMock; private readonly Mock wellServiceMock; private readonly Mock configurationMock; - private readonly Mock backgroundWorkerMock; - private readonly Mock emailService; + private readonly Mock backgroundWorkerMock; + private readonly Mock emailServiceMock; public DrillingProgramServiceTest() { @@ -102,7 +102,8 @@ namespace AsbCloudWebApi.Tests.ServicesTests userRepositoryMock = new Mock(); wellServiceMock = new Mock(); configurationMock = new Mock(); - backgroundWorkerMock = new Mock(); + backgroundWorkerMock = new Mock(); + emailServiceMock = new Mock(); } [Fact] @@ -115,7 +116,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var users = await service.GetAvailableUsers(idWell, CancellationToken.None); @@ -132,7 +133,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var result = await service.AddPartsAsync(idWell, new int[] { 1001, 1002 }, CancellationToken.None); @@ -151,7 +152,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var result = await service.RemovePartsAsync(idWell, new int[] { 1005 }, CancellationToken.None); @@ -174,7 +175,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var result = await service.AddUserAsync(idWell, 1001, publisher1.Id, 1, CancellationToken.None); @@ -209,7 +210,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var result = await service.RemoveUserAsync(idWell, idFileCategory, publisher1.Id, idUserRole, CancellationToken.None); @@ -235,7 +236,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var fileMark = new FileMarkDto { @@ -266,7 +267,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var fileMark = new FileMarkDto { IdFile = file1001.Id, @@ -304,7 +305,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var fileMark = new FileMarkDto { @@ -331,7 +332,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var state = await service.GetStateAsync(idWell, publisher1.Id, CancellationToken.None); @@ -358,12 +359,12 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var state = await service.GetStateAsync(idWell, publisher1.Id, CancellationToken.None); Assert.Equal(2, state.IdState); - backgroundWorkerMock.Verify(s => s.Enqueue(It.IsAny>())); + backgroundWorkerMock.Verify(s => s.Push(It.IsAny())); } [Fact] @@ -388,7 +389,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests wellServiceMock.Object, configurationMock.Object, backgroundWorkerMock.Object, - emailService.Object); + emailServiceMock.Object); var state = await service.GetStateAsync(idWell, publisher1.Id, CancellationToken.None); diff --git a/AsbCloudWebApi/Controllers/AdminClusterController.cs b/AsbCloudWebApi/Controllers/AdminClusterController.cs index 2f8d927a..511b786b 100644 --- a/AsbCloudWebApi/Controllers/AdminClusterController.cs +++ b/AsbCloudWebApi/Controllers/AdminClusterController.cs @@ -11,9 +11,9 @@ namespace AsbCloudWebApi.Controllers [Route("api/admin/cluster")] [ApiController] [Authorize] - public class AdminClusterController : CrudController> + public class AdminClusterController : CrudController> { - public AdminClusterController(ICrudService service) + public AdminClusterController(ICrudRepository service) : base(service) { } } diff --git a/AsbCloudWebApi/Controllers/AdminCompanyController.cs b/AsbCloudWebApi/Controllers/AdminCompanyController.cs index 04f6109f..f6ebb3d2 100644 --- a/AsbCloudWebApi/Controllers/AdminCompanyController.cs +++ b/AsbCloudWebApi/Controllers/AdminCompanyController.cs @@ -11,9 +11,9 @@ namespace AsbCloudWebApi.Controllers [Route("api/admin/company")] [ApiController] [Authorize] - public class AdminCompanyController : CrudController> + public class AdminCompanyController : CrudController> { - public AdminCompanyController(ICrudService service) + public AdminCompanyController(ICrudRepository service) : base(service) { } diff --git a/AsbCloudWebApi/Controllers/AdminCompanyTypeController.cs b/AsbCloudWebApi/Controllers/AdminCompanyTypeController.cs index 60c3ea22..eb05c245 100644 --- a/AsbCloudWebApi/Controllers/AdminCompanyTypeController.cs +++ b/AsbCloudWebApi/Controllers/AdminCompanyTypeController.cs @@ -11,9 +11,9 @@ namespace AsbCloudWebApi.Controllers [Route("api/admin/companyType")] [ApiController] [Authorize] - public class AdminCompanyTypeController : CrudController> + public class AdminCompanyTypeController : CrudController> { - public AdminCompanyTypeController(ICrudService service) + public AdminCompanyTypeController(ICrudRepository service) : base(service) { } } diff --git a/AsbCloudWebApi/Controllers/AdminDepositController.cs b/AsbCloudWebApi/Controllers/AdminDepositController.cs index c35be313..b39e4f01 100644 --- a/AsbCloudWebApi/Controllers/AdminDepositController.cs +++ b/AsbCloudWebApi/Controllers/AdminDepositController.cs @@ -11,9 +11,9 @@ namespace AsbCloudWebApi.Controllers [Route("api/admin/deposit")] [ApiController] [Authorize] - public class AdminDepositController : CrudController> + public class AdminDepositController : CrudController> { - public AdminDepositController(ICrudService service) + public AdminDepositController(ICrudRepository service) : base(service) { } diff --git a/AsbCloudWebApi/Controllers/AdminPermissionController.cs b/AsbCloudWebApi/Controllers/AdminPermissionController.cs index ffbfe213..31d7bec6 100644 --- a/AsbCloudWebApi/Controllers/AdminPermissionController.cs +++ b/AsbCloudWebApi/Controllers/AdminPermissionController.cs @@ -15,9 +15,9 @@ namespace AsbCloudWebApi.Controllers [Route("api/admin/permission")] [ApiController] [Authorize] - public class AdminPermissionController : CrudController> + public class AdminPermissionController : CrudController> { - public AdminPermissionController(ICrudService service) + public AdminPermissionController(ICrudRepository service) : base(service) { } diff --git a/AsbCloudWebApi/Controllers/AdminTelemetryController.cs b/AsbCloudWebApi/Controllers/AdminTelemetryController.cs index 1daad23c..b55d623a 100644 --- a/AsbCloudWebApi/Controllers/AdminTelemetryController.cs +++ b/AsbCloudWebApi/Controllers/AdminTelemetryController.cs @@ -13,11 +13,11 @@ namespace AsbCloudWebApi.Controllers [Route("api/admin/telemetry")] [ApiController] [Authorize] - public class AdminTelemetryController : CrudController> + public class AdminTelemetryController : CrudController> { private readonly ITelemetryService telemetryService; - public AdminTelemetryController(ICrudService service, + public AdminTelemetryController(ICrudRepository service, ITelemetryService telemetryService) : base(service) { diff --git a/AsbCloudWebApi/Controllers/AdminUserController.cs b/AsbCloudWebApi/Controllers/AdminUserController.cs index 0ce262d0..288f838a 100644 --- a/AsbCloudWebApi/Controllers/AdminUserController.cs +++ b/AsbCloudWebApi/Controllers/AdminUserController.cs @@ -13,7 +13,7 @@ namespace AsbCloudWebApi.Controllers [Route("api/admin/user")] [ApiController] [Authorize] - public class AdminUserController : CrudController> + public class AdminUserController : CrudController> { public AdminUserController(IUserRepository service) : base(service) diff --git a/AsbCloudWebApi/Controllers/AdminWellController.cs b/AsbCloudWebApi/Controllers/AdminWellController.cs index 3441af43..4a670652 100644 --- a/AsbCloudWebApi/Controllers/AdminWellController.cs +++ b/AsbCloudWebApi/Controllers/AdminWellController.cs @@ -13,7 +13,7 @@ namespace AsbCloudWebApi.Controllers [Route("api/admin/well")] [ApiController] [Authorize] - public class AdminWellController : CrudController> + public class AdminWellController : CrudController> { public AdminWellController(IWellService service) : base(service) diff --git a/AsbCloudWebApi/Controllers/CrudController.cs b/AsbCloudWebApi/Controllers/CrudController.cs index db66e468..4b0a6c19 100644 --- a/AsbCloudWebApi/Controllers/CrudController.cs +++ b/AsbCloudWebApi/Controllers/CrudController.cs @@ -21,7 +21,7 @@ namespace AsbCloudWebApi.Controllers [Authorize] public abstract class CrudController : ControllerBase where T : IId - where TService : ICrudService + where TService : ICrudRepository { protected readonly TService service; @@ -115,7 +115,7 @@ namespace AsbCloudWebApi.Controllers return Forbid(); var result = await service.UpdateAsync(value, token).ConfigureAwait(false); - if (result == ICrudService.ErrorIdNotFound) + if (result == ICrudRepository.ErrorIdNotFound) return BadRequest($"id:{value.Id} does not exist in the db"); return Ok(result); } @@ -134,7 +134,7 @@ namespace AsbCloudWebApi.Controllers return Forbid(); var result = await service.DeleteAsync(id, token).ConfigureAwait(false); - if (result == ICrudService.ErrorIdNotFound) + if (result == ICrudRepository.ErrorIdNotFound) return NoContent(); return Ok(result); } diff --git a/AsbCloudWebApi/Controllers/DrillerController.cs b/AsbCloudWebApi/Controllers/DrillerController.cs index 2175c1ce..0fa84e4a 100644 --- a/AsbCloudWebApi/Controllers/DrillerController.cs +++ b/AsbCloudWebApi/Controllers/DrillerController.cs @@ -11,9 +11,9 @@ namespace AsbCloudWebApi.Controllers [Route("api/driller")] [ApiController] [Authorize] - public class DrillerController : CrudController> + public class DrillerController : CrudController> { - public DrillerController(ICrudService service) + public DrillerController(ICrudRepository service) : base(service) { } } diff --git a/AsbCloudWebApi/Controllers/FileCategoryController.cs b/AsbCloudWebApi/Controllers/FileCategoryController.cs index 5567c5e7..2a19cbe7 100644 --- a/AsbCloudWebApi/Controllers/FileCategoryController.cs +++ b/AsbCloudWebApi/Controllers/FileCategoryController.cs @@ -11,9 +11,9 @@ namespace AsbCloudWebApi.Controllers [Route("api/[Controller]")] [ApiController] [Authorize] - public class FileCategoryController : CrudController> + public class FileCategoryController : CrudController> { - public FileCategoryController(ICrudService service, IFileCategoryService fileCategoryService) + public FileCategoryController(ICrudRepository service, IFileCategoryService fileCategoryService) : base(service) { } diff --git a/AsbCloudWebApi/Controllers/LimitingParameterController.cs b/AsbCloudWebApi/Controllers/LimitingParameterController.cs index 1df008e8..9bd5784d 100644 --- a/AsbCloudWebApi/Controllers/LimitingParameterController.cs +++ b/AsbCloudWebApi/Controllers/LimitingParameterController.cs @@ -40,6 +40,18 @@ namespace AsbCloudWebApi.Controllers return Ok(subsystemResult); } + /// + /// Получение словаря названий ограничений + /// + /// + [HttpGet("names")] + [ProducesResponseType(typeof(Dictionary), (int)System.Net.HttpStatusCode.OK)] + public IActionResult GetLimitingParameteraNames() + { + var feedRegulatorData = limitingParameterService.GetLimitingParameteraNames(); + return Ok(feedRegulatorData); + } + protected async Task UserHasAccesToWellAsync(int idWell, CancellationToken token) { var idCompany = User.GetCompanyId(); diff --git a/AsbCloudWebApi/Controllers/ReportController.cs b/AsbCloudWebApi/Controllers/ReportController.cs index d3df1092..8b1081ab 100644 --- a/AsbCloudWebApi/Controllers/ReportController.cs +++ b/AsbCloudWebApi/Controllers/ReportController.cs @@ -68,7 +68,7 @@ namespace AsbCloudWebApi.Controllers ).ConfigureAwait(false); }, token); - var id = reportService.CreateReport(idWell, (int)idUser, + var id = reportService.EnqueueCreateReportWork(idWell, (int)idUser, stepSeconds, format, begin, end, HandleReportProgressAsync); return Ok(id); diff --git a/AsbCloudWebApi/Controllers/Subsystems/AdminSubsystemController.cs b/AsbCloudWebApi/Controllers/Subsystems/AdminSubsystemController.cs index dc7ac408..454062e1 100644 --- a/AsbCloudWebApi/Controllers/Subsystems/AdminSubsystemController.cs +++ b/AsbCloudWebApi/Controllers/Subsystems/AdminSubsystemController.cs @@ -12,9 +12,9 @@ namespace AsbCloudWebApi.Controllers.Subsystems [Route("api/admin/subsystem")] [ApiController] [Authorize] - public class AdminSubsystemController : CrudController> + public class AdminSubsystemController : CrudController> { - public AdminSubsystemController(ICrudService service) + public AdminSubsystemController(ICrudRepository service) : base(service) { } diff --git a/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs b/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs index e2b1a945..65f503ea 100644 --- a/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs +++ b/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs @@ -3,7 +3,6 @@ using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudApp.Services.Subsystems; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Threading; @@ -25,6 +24,14 @@ namespace AsbCloudWebApi.Controllers.Subsystems private readonly ISubsystemOperationTimeService subsystemOperationTimeService; private readonly IWellService wellService; private readonly ISubsystemService subsystemService; + + private readonly Dictionary subsystemNames = new() + { + { 1, "SAUB" }, + { 65537, "Torque Master" }, + { 65536, "Spin Master" } + }; + public SubsystemOperationTimeController(ISubsystemOperationTimeService subsystemOperationTimeService, IWellService wellService, ISubsystemService subsystemService) { this.subsystemOperationTimeService = subsystemOperationTimeService; @@ -131,6 +138,17 @@ namespace AsbCloudWebApi.Controllers.Subsystems return Ok(result); } + /// + /// Получение словаря названий подсистем + /// + /// + [HttpGet("names")] + [ProducesResponseType(typeof(Dictionary), (int)System.Net.HttpStatusCode.OK)] + public IActionResult GetSubsystemsNames() + { + return Ok(subsystemNames); + } + protected async Task UserHasAccesToWellAsync(int idWell, CancellationToken token) { var idCompany = User.GetCompanyId(); diff --git a/AsbCloudWebApi/Middlewares/RequerstTrackerMiddleware.cs b/AsbCloudWebApi/Middlewares/RequerstTrackerMiddleware.cs index b35b40e5..551b4d03 100644 --- a/AsbCloudWebApi/Middlewares/RequerstTrackerMiddleware.cs +++ b/AsbCloudWebApi/Middlewares/RequerstTrackerMiddleware.cs @@ -43,6 +43,7 @@ namespace AsbCloudWebApi.Middlewares sw.Stop(); requestLog.ElapsedMilliseconds = sw.ElapsedMilliseconds; requestLog.Status = context.Response.StatusCode; + // TODO: Add request params and body size. service.RegisterRequestError(requestLog, ex); throw; } diff --git a/AsbCloudWebApi/Middlewares/SimplifyExceptionsMiddleware.cs b/AsbCloudWebApi/Middlewares/SimplifyExceptionsMiddleware.cs index c09f9749..dd9fff9f 100644 --- a/AsbCloudWebApi/Middlewares/SimplifyExceptionsMiddleware.cs +++ b/AsbCloudWebApi/Middlewares/SimplifyExceptionsMiddleware.cs @@ -40,7 +40,7 @@ namespace AsbCloudWebApi.Middlewares { Console.WriteLine(ex.Message); } - catch (Exception ex) + catch (Exception ex) // TODO: find explicit exception. Use Trace. Add body size to message. { if (ex.Message.Contains("Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate.")) Console.WriteLine("Reading the request body timed out due to data arriving too slowly."); diff --git a/AsbCloudWebApi/Program.cs b/AsbCloudWebApi/Program.cs index b21fa05f..18825902 100644 --- a/AsbCloudWebApi/Program.cs +++ b/AsbCloudWebApi/Program.cs @@ -1,10 +1,5 @@ -using DocumentFormat.OpenXml.InkML; using Microsoft.AspNetCore.Hosting; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using System; -using System.Linq; namespace AsbCloudWebApi { @@ -15,30 +10,8 @@ namespace AsbCloudWebApi public static void Main(string[] args) { - IConfigurationRoot configuration = new ConfigurationBuilder() - .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) - .AddJsonFile("appsettings.json") - .Build(); - - if (args?.Length > 0) - { - if (args.Contains("db_init")) - { - var connectionStringName = "DefaultConnection"; - - var context = AsbCloudInfrastructure.DependencyInjection.MakeContext(configuration.GetConnectionString(connectionStringName)); - context.Database.SetCommandTimeout(TimeSpan.FromSeconds(5 * 60)); - context.Database.Migrate(); - - Console.WriteLine(" ."); - return; - } - WriteHelp(); - return; - } - var host = CreateHostBuilder(args).Build(); - AsbCloudInfrastructure.Startup.BeforeRunHandler(host, configuration); + AsbCloudInfrastructure.Startup.BeforeRunHandler(host); host.Run(); } @@ -48,17 +21,5 @@ namespace AsbCloudWebApi { webBuilder.UseStartup(); }); - - private static void WriteHelp() - { - Console.WriteLine(" ."); - Console.WriteLine(" :"); - Console.WriteLine("db_init - ."); - Console.WriteLine(" \"DefaultConnection\""); - Console.WriteLine(" , "); - Console.WriteLine(" , ."); - Console.WriteLine(" public"); - Console.WriteLine(""); - } } }