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
///
///
- 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
///
///
- 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