From 421ab4185513aa49360aef509665cfdffbd2956d Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Thu, 9 Jan 2025 09:29:42 +0500 Subject: [PATCH 01/16] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20TagBag,=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20Date=20=D0=BD=D0=B0=20Timestamp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DD.Persistence.Database/Entity/DataSaub.cs | 63 ------------------- .../Entity/ITimestampedData.cs | 2 +- DD.Persistence.Database/Entity/TagBag.cs | 18 ++++++ .../Controllers/DataSaubControllerTest.cs | 2 +- .../TimeSeriesBaseControllerTest.cs | 6 +- .../DependencyInjection.cs | 4 +- .../Extensions/EFExtensionsSortBy.cs | 6 +- ...aRepository.cs => TagBagDataRepository.cs} | 16 +++-- 8 files changed, 35 insertions(+), 82 deletions(-) delete mode 100644 DD.Persistence.Database/Entity/DataSaub.cs create mode 100644 DD.Persistence.Database/Entity/TagBag.cs rename DD.Persistence.Repository/Repositories/{TimeSeriesDataRepository.cs => TagBagDataRepository.cs} (82%) diff --git a/DD.Persistence.Database/Entity/DataSaub.cs b/DD.Persistence.Database/Entity/DataSaub.cs deleted file mode 100644 index d4515b2..0000000 --- a/DD.Persistence.Database/Entity/DataSaub.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace DD.Persistence.Database.Model; -public class DataSaub : ITimestampedData -{ - [Key, Column("date")] - public DateTimeOffset Date { get; set; } - - [Column("mode")] - public int? Mode { get; set; } - - [Column("user")] - public string? User { get; set; } - - [Column("wellDepth")] - public double? WellDepth { get; set; } - - [Column("bitDepth")] - public double? BitDepth { get; set; } - - [Column("blockPosition")] - public double? BlockPosition { get; set; } - - [Column("blockSpeed")] - public double? BlockSpeed { get; set; } - - [Column("pressure")] - public double? Pressure { get; set; } - - [Column("axialLoad")] - public double? AxialLoad { get; set; } - - [Column("hookWeight")] - public double? HookWeight { get; set; } - - [Column("rotorTorque")] - public double? RotorTorque { get; set; } - - [Column("rotorSpeed")] - public double? RotorSpeed { get; set; } - - [Column("flow")] - public double? Flow { get; set; } - - [Column("mseState")] - public short MseState { get; set; } - - [Column("idFeedRegulator")] - public int IdFeedRegulator { get; set; } - - [Column("mse")] - public double? Mse { get; set; } - - [Column("pump0Flow")] - public double? Pump0Flow { get; set; } - - [Column("pump1Flow")] - public double? Pump1Flow { get; set; } - - [Column("pump2Flow")] - public double? Pump2Flow { get; set; } -} diff --git a/DD.Persistence.Database/Entity/ITimestampedData.cs b/DD.Persistence.Database/Entity/ITimestampedData.cs index ce5468c..5a1b25c 100644 --- a/DD.Persistence.Database/Entity/ITimestampedData.cs +++ b/DD.Persistence.Database/Entity/ITimestampedData.cs @@ -4,5 +4,5 @@ public interface ITimestampedData /// /// Дата (должна быть обязательно в UTC) /// - DateTimeOffset Date { get; set; } + DateTimeOffset Timestamp { get; set; } } diff --git a/DD.Persistence.Database/Entity/TagBag.cs b/DD.Persistence.Database/Entity/TagBag.cs new file mode 100644 index 0000000..2aa7546 --- /dev/null +++ b/DD.Persistence.Database/Entity/TagBag.cs @@ -0,0 +1,18 @@ +using DD.Persistence.Database.Model; +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace DD.Persistence.Database.Entity; + +[PrimaryKey(nameof(BagId), nameof(Timestamp))] +public class TagBag : ITimestampedData +{ + [Comment("Временная отметка"), Key] + public DateTimeOffset Timestamp { get; set; } + + public Guid BagId { get; set; } + + [Column(TypeName = "jsonb")] + public required object[] Values { get; set; } +} diff --git a/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs index 7daad50..3429651 100644 --- a/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs @@ -34,7 +34,7 @@ public class DataSaubControllerTest : TimeSeriesBaseControllerTest : BaseIntegrat var datesRangeExpected = 30; var entity2 = entity.Adapt(); - entity2.Date = entity.Date.AddDays(datesRangeExpected); + entity2.Timestamp = entity.Timestamp.AddDays(datesRangeExpected); var dbset = dbContext.Set(); dbset.Add(entity); @@ -86,7 +86,7 @@ public abstract class TimeSeriesBaseControllerTest : BaseIntegrat for (var i = 1; i <= differenceBetweenStartAndEndDays; i++) { var entity2 = entity.Adapt(); - entity2.Date = entity.Date.AddDays(i - 1); + entity2.Timestamp = entity.Timestamp.AddDays(i - 1); entities.Add(entity2); } @@ -96,7 +96,7 @@ public abstract class TimeSeriesBaseControllerTest : BaseIntegrat dbContext.SaveChanges(); - var response = await timeSeriesClient.GetResampledData(entity.Date.AddMinutes(-1), differenceBetweenStartAndEndDays * 24 * 60 * 60 + 60, approxPointsCount, new CancellationToken()); + var response = await timeSeriesClient.GetResampledData(entity.Timestamp.AddMinutes(-1), differenceBetweenStartAndEndDays * 24 * 60 * 60 + 60, approxPointsCount, new CancellationToken()); //assert Assert.NotNull(response); diff --git a/DD.Persistence.Repository/DependencyInjection.cs b/DD.Persistence.Repository/DependencyInjection.cs index df15095..5114cd0 100644 --- a/DD.Persistence.Repository/DependencyInjection.cs +++ b/DD.Persistence.Repository/DependencyInjection.cs @@ -35,9 +35,9 @@ public static class DependencyInjection MapsterSetup(); - services.AddTransient, TimeSeriesDataRepository>(); + services.AddTransient, TagBagDataRepository>(); services.AddTransient(); - services.AddTransient, TimeSeriesDataCachedRepository>(); + //services.AddTransient, TimeSeriesDataCachedRepository>(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/DD.Persistence.Repository/Extensions/EFExtensionsSortBy.cs b/DD.Persistence.Repository/Extensions/EFExtensionsSortBy.cs index bdfc8a2..bc03e3b 100644 --- a/DD.Persistence.Repository/Extensions/EFExtensionsSortBy.cs +++ b/DD.Persistence.Repository/Extensions/EFExtensionsSortBy.cs @@ -71,7 +71,7 @@ public static class EFExtensionsSortBy /// и опционально указания направления сортировки "asc" или "desc" /// /// - /// var query = query("Date desc"); + /// var query = query("Timestamp desc"); /// /// Запрос с примененной сортировкой public static IOrderedQueryable SortBy( @@ -103,7 +103,7 @@ public static class EFExtensionsSortBy /// и опционально указания направления сортировки "asc" или "desc" /// /// - /// var query = query("Date desc"); + /// var query = query("Timestamp desc"); /// /// Запрос с примененной сортировкой public static IOrderedQueryable SortBy( @@ -129,7 +129,7 @@ public static class EFExtensionsSortBy /// и опционально указания направления сортировки "asc" или "desc" /// /// - /// var query = query("Date desc"); + /// var query = query("Timestamp desc"); /// /// Запрос с примененной сортировкой public static IOrderedQueryable ThenSortBy( diff --git a/DD.Persistence.Repository/Repositories/TimeSeriesDataRepository.cs b/DD.Persistence.Repository/Repositories/TagBagDataRepository.cs similarity index 82% rename from DD.Persistence.Repository/Repositories/TimeSeriesDataRepository.cs rename to DD.Persistence.Repository/Repositories/TagBagDataRepository.cs index c4c634b..320b99b 100644 --- a/DD.Persistence.Repository/Repositories/TimeSeriesDataRepository.cs +++ b/DD.Persistence.Repository/Repositories/TagBagDataRepository.cs @@ -5,13 +5,11 @@ using DD.Persistence.Models; using DD.Persistence.Repositories; namespace DD.Persistence.Repository.Repositories; -public class TimeSeriesDataRepository : ITimeSeriesDataRepository - where TEntity : class, ITimestampedData, new() - where TDto : class, ITimeSeriesAbstractDto, new() +public class TagBagRepository : ITimeSeriesDataRepository { private readonly DbContext db; - public TimeSeriesDataRepository(DbContext db) + public TagBagRepository(DbContext db) { this.db = db; } @@ -21,8 +19,8 @@ public class TimeSeriesDataRepository : ITimeSeriesDataRepository public virtual async Task GetDatesRange(CancellationToken token) { var query = GetQueryReadOnly(); - var minDate = await query.MinAsync(o => o.Date, token); - var maxDate = await query.MaxAsync(o => o.Date, token); + var minDate = await query.MinAsync(o => o.Timestamp, token); + var maxDate = await query.MaxAsync(o => o.Timestamp, token); return new DatesRangeDto { @@ -33,7 +31,7 @@ public class TimeSeriesDataRepository : ITimeSeriesDataRepository public virtual async Task> GetGtDate(DateTimeOffset date, CancellationToken token) { - var query = this.db.Set().Where(e => e.Date > date); + var query = this.db.Set().Where(e => e.Timestamp > date); var entities = await query.ToArrayAsync(token); var dtos = entities.Select(e => e.Adapt()); @@ -54,7 +52,7 @@ public class TimeSeriesDataRepository : ITimeSeriesDataRepository protected async Task> GetLastAsync(int takeCount, CancellationToken token) { var query = GetQueryReadOnly() - .OrderByDescending(e => e.Date) + .OrderByDescending(e => e.Timestamp) .Take(takeCount); var entities = await query.ToArrayAsync(token); @@ -66,7 +64,7 @@ public class TimeSeriesDataRepository : ITimeSeriesDataRepository protected async Task GetFirstAsync(CancellationToken token) { var query = GetQueryReadOnly() - .OrderBy(e => e.Date); + .OrderBy(e => e.Timestamp); var entity = await query.FirstOrDefaultAsync(token); -- 2.45.2 From 750788d550704340e1808fed1974b290109a4b4e Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Mon, 13 Jan 2025 17:45:49 +0500 Subject: [PATCH 02/16] =?UTF-8?q?=D0=9D=D0=B0=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ChangeLogController.cs | 1 + .../Controllers/DataSaubController.cs | 2 +- .../Controllers/SetpointController.cs | 1 + ...eriesController.cs => TagBagController.cs} | 8 +- .../Controllers/TechMessagesController.cs | 1 + .../Controllers/TimestampedSetController.cs | 1 + .../Controllers/WitsDataController.cs | 1 + .../Clients/ChangeLogClient.cs | 1 + .../Clients/Interfaces/IChangeLogClient.cs | 1 + .../Clients/Interfaces/ISetpointClient.cs | 1 + .../Clients/Interfaces/ITechMessagesClient.cs | 1 + .../Clients/Interfaces/ITimeSeriesClient.cs | 5 +- .../Interfaces/ITimestampedSetClient.cs | 1 + .../Clients/Interfaces/IWitsDataClient.cs | 1 + .../Interfaces/Refit/IRefitChangeLogClient.cs | 1 + .../Interfaces/Refit/IRefitSetpointClient.cs | 1 + .../Refit/IRefitTechMessagesClient.cs | 1 + .../Refit/IRefitTimeSeriesClient.cs | 5 +- .../Refit/IRefitTimestampedSetClient.cs | 1 + .../Interfaces/Refit/IRefitWitsDataClient.cs | 1 + .../Clients/SetpointClient.cs | 1 + .../Clients/TechMessagesClient.cs | 1 + .../Clients/TimeSeriesClient.cs | 5 +- .../Clients/TimestampedSetClient.cs | 1 + .../Clients/WitsDataClient.cs | 1 + DD.Persistence.Database/Entity/ChangeLog.cs | 3 +- .../Entity/ParameterData.cs | 5 +- DD.Persistence.Database/Entity/Setpoint.cs | 9 +- DD.Persistence.Database/Entity/TechMessage.cs | 3 +- .../Entity/TimestampedSet.cs | 11 -- .../{TagBag.cs => TimestampedValues.cs} | 11 +- .../IChangeLog.cs | 3 +- .../ITimestampedItem.cs} | 4 +- .../TimeSeriesBaseControllerTest.cs | 8 +- .../DependencyInjection.cs | 5 +- DD.Persistence.Repository/QueryBuilders.cs | 5 +- .../Repositories/ChangeLogRepository.cs | 1 + .../Repositories/ParameterRepository.cs | 1 + .../Repositories/SetpointRepository.cs | 13 ++- .../Repositories/TechMessagesRepository.cs | 3 +- .../TimeSeriesDataCachedRepository.cs | 103 ----------------- .../Repositories/TimestampedSetRepository.cs | 1 + ...tory.cs => TimestampedValuesRepository.cs} | 24 ++-- .../DataSourceSystemCachedRepository.cs | 3 +- .../TimestampedValuesCachedRepository.cs | 104 ++++++++++++++++++ DD.Persistence/API/ISyncApi.cs | 2 +- .../API/ISyncWithDiscriminatorApi.cs | 2 +- DD.Persistence/API/ITimeSeriesDataApi.cs | 4 +- DD.Persistence/EFExtensions.cs | 2 +- .../Models/{ => Common}/DatesRangeDto.cs | 2 +- .../{ => Common}/PaginationContainer.cs | 2 +- DD.Persistence/Models/DataSaubDto.cs | 41 ------- DD.Persistence/Models/TimestampedSetDto.cs | 17 ++- .../ITimestampAbstractDto.cs} | 6 +- .../IWithSectionPart.cs | 2 +- .../Repositories/IChangeLogRepository.cs | 1 + .../Repositories/IParameterRepository.cs | 1 + .../Repositories/ISetpointRepository.cs | 1 + .../Repositories/ISyncRepository.cs | 2 +- .../ISyncWithDiscriminatorRepository.cs | 2 +- .../Repositories/ITechMessagesRepository.cs | 1 + .../Repositories/ITimeSeriesBaseRepository.cs | 9 +- .../Repositories/ITimestampedSetRepository.cs | 1 + ...ory.cs => ITimestampedValuesRepository.cs} | 7 +- .../Services/Interfaces/IWitsDataService.cs | 1 + DD.Persistence/Services/WitsDataService.cs | 1 + 66 files changed, 238 insertions(+), 234 deletions(-) rename DD.Persistence.API/Controllers/{TimeSeriesController.cs => TagBagController.cs} (90%) delete mode 100644 DD.Persistence.Database/Entity/TimestampedSet.cs rename DD.Persistence.Database/Entity/{TagBag.cs => TimestampedValues.cs} (51%) rename DD.Persistence.Database/{Entity => EntityAbstractions}/IChangeLog.cs (95%) rename DD.Persistence.Database/{Entity/ITimestampedData.cs => EntityAbstractions/ITimestampedItem.cs} (63%) delete mode 100644 DD.Persistence.Repository/Repositories/TimeSeriesDataCachedRepository.cs rename DD.Persistence.Repository/Repositories/{TagBagDataRepository.cs => TimestampedValuesRepository.cs} (76%) rename DD.Persistence.Repository/{Repositories => RepositoriesCached}/DataSourceSystemCachedRepository.cs (92%) create mode 100644 DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs rename DD.Persistence/Models/{ => Common}/DatesRangeDto.cs (89%) rename DD.Persistence/Models/{ => Common}/PaginationContainer.cs (95%) delete mode 100644 DD.Persistence/Models/DataSaubDto.cs rename DD.Persistence/{Models/ITimeSeriesAbstractDto.cs => ModelsAbstractions/ITimestampAbstractDto.cs} (59%) rename DD.Persistence/{Models => ModelsAbstractions}/IWithSectionPart.cs (77%) rename DD.Persistence/Repositories/{ITimeSeriesDataRepository.cs => ITimestampedValuesRepository.cs} (59%) diff --git a/DD.Persistence.API/Controllers/ChangeLogController.cs b/DD.Persistence.API/Controllers/ChangeLogController.cs index 21761e1..8b788c4 100644 --- a/DD.Persistence.API/Controllers/ChangeLogController.cs +++ b/DD.Persistence.API/Controllers/ChangeLogController.cs @@ -4,6 +4,7 @@ using DD.Persistence.Models; using DD.Persistence.Models.Requests; using DD.Persistence.Repositories; using System.Net; +using DD.Persistence.Models.Common; namespace DD.Persistence.API.Controllers; diff --git a/DD.Persistence.API/Controllers/DataSaubController.cs b/DD.Persistence.API/Controllers/DataSaubController.cs index 832faec..df86844 100644 --- a/DD.Persistence.API/Controllers/DataSaubController.cs +++ b/DD.Persistence.API/Controllers/DataSaubController.cs @@ -13,7 +13,7 @@ namespace DD.Persistence.API.Controllers; [Route("api/[controller]")] public class DataSaubController : TimeSeriesController { - public DataSaubController(ITimeSeriesDataRepository timeSeriesDataRepository) : base(timeSeriesDataRepository) + public DataSaubController(ITimestampedValuesRepository timeSeriesDataRepository) : base(timeSeriesDataRepository) { } diff --git a/DD.Persistence.API/Controllers/SetpointController.cs b/DD.Persistence.API/Controllers/SetpointController.cs index e3b8b14..663a1c4 100644 --- a/DD.Persistence.API/Controllers/SetpointController.cs +++ b/DD.Persistence.API/Controllers/SetpointController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using DD.Persistence.Models; using DD.Persistence.Repositories; using System.Net; +using DD.Persistence.Models.Common; namespace DD.Persistence.API.Controllers; diff --git a/DD.Persistence.API/Controllers/TimeSeriesController.cs b/DD.Persistence.API/Controllers/TagBagController.cs similarity index 90% rename from DD.Persistence.API/Controllers/TimeSeriesController.cs rename to DD.Persistence.API/Controllers/TagBagController.cs index 5fded36..c7b8475 100644 --- a/DD.Persistence.API/Controllers/TimeSeriesController.cs +++ b/DD.Persistence.API/Controllers/TagBagController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Models; using DD.Persistence.Repositories; +using DD.Persistence.ModelsAbstractions; namespace DD.Persistence.API.Controllers; @@ -9,11 +9,11 @@ namespace DD.Persistence.API.Controllers; [Authorize] [Route("api/[controller]")] public class TimeSeriesController : ControllerBase, ITimeSeriesDataApi - where TDto : class, ITimeSeriesAbstractDto, new() + where TDto : class, ITimestampAbstractDto, new() { - private readonly ITimeSeriesDataRepository timeSeriesDataRepository; + private readonly ITimestampedValuesRepository timeSeriesDataRepository; - public TimeSeriesController(ITimeSeriesDataRepository timeSeriesDataRepository) + public TimeSeriesController(ITimestampedValuesRepository timeSeriesDataRepository) { this.timeSeriesDataRepository = timeSeriesDataRepository; } diff --git a/DD.Persistence.API/Controllers/TechMessagesController.cs b/DD.Persistence.API/Controllers/TechMessagesController.cs index 81663e0..cd98e42 100644 --- a/DD.Persistence.API/Controllers/TechMessagesController.cs +++ b/DD.Persistence.API/Controllers/TechMessagesController.cs @@ -4,6 +4,7 @@ using DD.Persistence.Models; using DD.Persistence.Models.Requests; using DD.Persistence.Repositories; using System.Net; +using DD.Persistence.Models.Common; namespace DD.Persistence.API.Controllers; diff --git a/DD.Persistence.API/Controllers/TimestampedSetController.cs b/DD.Persistence.API/Controllers/TimestampedSetController.cs index 0c805ab..c4514f5 100644 --- a/DD.Persistence.API/Controllers/TimestampedSetController.cs +++ b/DD.Persistence.API/Controllers/TimestampedSetController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using DD.Persistence.Models; using DD.Persistence.Repositories; using System.Net; +using DD.Persistence.Models.Common; namespace DD.Persistence.API.Controllers; diff --git a/DD.Persistence.API/Controllers/WitsDataController.cs b/DD.Persistence.API/Controllers/WitsDataController.cs index 5df6c3f..c87345e 100644 --- a/DD.Persistence.API/Controllers/WitsDataController.cs +++ b/DD.Persistence.API/Controllers/WitsDataController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using DD.Persistence.Models; using DD.Persistence.Services.Interfaces; using System.Net; +using DD.Persistence.Models.Common; namespace DD.Persistence.API.Controllers; diff --git a/DD.Persistence.Client/Clients/ChangeLogClient.cs b/DD.Persistence.Client/Clients/ChangeLogClient.cs index 38c2ec4..e4f5904 100644 --- a/DD.Persistence.Client/Clients/ChangeLogClient.cs +++ b/DD.Persistence.Client/Clients/ChangeLogClient.cs @@ -4,6 +4,7 @@ using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Models; using DD.Persistence.Models.Requests; using DD.Persistence.Client.Clients.Interfaces.Refit; +using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients; public class ChangeLogClient : BaseClient, IChangeLogClient diff --git a/DD.Persistence.Client/Clients/Interfaces/IChangeLogClient.cs b/DD.Persistence.Client/Clients/Interfaces/IChangeLogClient.cs index 19edeab..f81ac8d 100644 --- a/DD.Persistence.Client/Clients/Interfaces/IChangeLogClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/IChangeLogClient.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; using DD.Persistence.Models.Requests; namespace DD.Persistence.Client.Clients.Interfaces; diff --git a/DD.Persistence.Client/Clients/Interfaces/ISetpointClient.cs b/DD.Persistence.Client/Clients/Interfaces/ISetpointClient.cs index 86462ea..5745e7e 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ISetpointClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ISetpointClient.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients.Interfaces; diff --git a/DD.Persistence.Client/Clients/Interfaces/ITechMessagesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITechMessagesClient.cs index a27e553..2af1af6 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITechMessagesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITechMessagesClient.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; using DD.Persistence.Models.Requests; namespace DD.Persistence.Client.Clients.Interfaces; diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimeSeriesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimeSeriesClient.cs index db619e5..c41564c 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimeSeriesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimeSeriesClient.cs @@ -1,4 +1,5 @@ -using DD.Persistence.Models; +using DD.Persistence.Models.Common; +using DD.Persistence.ModelsAbstractions; namespace DD.Persistence.Client.Clients.Interfaces; @@ -6,7 +7,7 @@ namespace DD.Persistence.Client.Clients.Interfaces; /// Клиент для работы с временными данными /// /// -public interface ITimeSeriesClient : IDisposable where TDto : class, ITimeSeriesAbstractDto +public interface ITimeSeriesClient : IDisposable where TDto : class, ITimestampAbstractDto { /// /// Добавление записей diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs index be98a77..5efd6e7 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients.Interfaces; diff --git a/DD.Persistence.Client/Clients/Interfaces/IWitsDataClient.cs b/DD.Persistence.Client/Clients/Interfaces/IWitsDataClient.cs index e954484..0ac015d 100644 --- a/DD.Persistence.Client/Clients/Interfaces/IWitsDataClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/IWitsDataClient.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; using Refit; namespace DD.Persistence.Client.Clients.Interfaces; diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitChangeLogClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitChangeLogClient.cs index 83c240f..8e35b4e 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitChangeLogClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitChangeLogClient.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; using DD.Persistence.Models.Requests; using Refit; diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitSetpointClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitSetpointClient.cs index 1acb398..8f75dfa 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitSetpointClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitSetpointClient.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; using Refit; namespace DD.Persistence.Client.Clients.Interfaces.Refit; diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTechMessagesClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTechMessagesClient.cs index 1fe63c1..862d1c7 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTechMessagesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTechMessagesClient.cs @@ -2,6 +2,7 @@ using DD.Persistence.Models; using DD.Persistence.Models.Requests; using Refit; +using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients.Interfaces.Refit { diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimeSeriesClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimeSeriesClient.cs index 832cfde..0dc4994 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimeSeriesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimeSeriesClient.cs @@ -1,9 +1,10 @@ -using DD.Persistence.Models; +using DD.Persistence.Models.Common; +using DD.Persistence.ModelsAbstractions; using Refit; namespace DD.Persistence.Client.Clients.Interfaces.Refit; public interface IRefitTimeSeriesClient : IRefitClient, IDisposable - where TDto : class, ITimeSeriesAbstractDto + where TDto : class, ITimestampAbstractDto { private const string BaseRoute = "/api/dataSaub"; diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs index 6211d2d..921e867 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; using Refit; namespace DD.Persistence.Client.Clients.Interfaces.Refit; diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitWitsDataClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitWitsDataClient.cs index e7e6d9a..9f719c6 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitWitsDataClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitWitsDataClient.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using DD.Persistence.Models; using Refit; +using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients.Interfaces.Refit; public interface IRefitWitsDataClient : IRefitClient, IDisposable diff --git a/DD.Persistence.Client/Clients/SetpointClient.cs b/DD.Persistence.Client/Clients/SetpointClient.cs index 808f0b9..c43a0b2 100644 --- a/DD.Persistence.Client/Clients/SetpointClient.cs +++ b/DD.Persistence.Client/Clients/SetpointClient.cs @@ -3,6 +3,7 @@ using DD.Persistence.Client.Clients.Base; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients; diff --git a/DD.Persistence.Client/Clients/TechMessagesClient.cs b/DD.Persistence.Client/Clients/TechMessagesClient.cs index e23c961..681e275 100644 --- a/DD.Persistence.Client/Clients/TechMessagesClient.cs +++ b/DD.Persistence.Client/Clients/TechMessagesClient.cs @@ -4,6 +4,7 @@ using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Models; using DD.Persistence.Models.Requests; +using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients; diff --git a/DD.Persistence.Client/Clients/TimeSeriesClient.cs b/DD.Persistence.Client/Clients/TimeSeriesClient.cs index c75ec81..10ff2e4 100644 --- a/DD.Persistence.Client/Clients/TimeSeriesClient.cs +++ b/DD.Persistence.Client/Clients/TimeSeriesClient.cs @@ -2,10 +2,11 @@ using DD.Persistence.Client.Clients.Base; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; -using DD.Persistence.Models; +using DD.Persistence.Models.Common; +using DD.Persistence.ModelsAbstractions; namespace DD.Persistence.Client.Clients; -public class TimeSeriesClient : BaseClient, ITimeSeriesClient where TDto : class, ITimeSeriesAbstractDto +public class TimeSeriesClient : BaseClient, ITimeSeriesClient where TDto : class, ITimestampAbstractDto { private readonly IRefitTimeSeriesClient timeSeriesClient; diff --git a/DD.Persistence.Client/Clients/TimestampedSetClient.cs b/DD.Persistence.Client/Clients/TimestampedSetClient.cs index 7d1ba56..d9f71dd 100644 --- a/DD.Persistence.Client/Clients/TimestampedSetClient.cs +++ b/DD.Persistence.Client/Clients/TimestampedSetClient.cs @@ -3,6 +3,7 @@ using DD.Persistence.Client.Clients.Base; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients; public class TimestampedSetClient : BaseClient, ITimestampedSetClient diff --git a/DD.Persistence.Client/Clients/WitsDataClient.cs b/DD.Persistence.Client/Clients/WitsDataClient.cs index e2703dc..d47f771 100644 --- a/DD.Persistence.Client/Clients/WitsDataClient.cs +++ b/DD.Persistence.Client/Clients/WitsDataClient.cs @@ -3,6 +3,7 @@ using DD.Persistence.Client.Clients.Base; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients; public class WitsDataClient : BaseClient, IWitsDataClient diff --git a/DD.Persistence.Database/Entity/ChangeLog.cs b/DD.Persistence.Database/Entity/ChangeLog.cs index 439e886..d874ea2 100644 --- a/DD.Persistence.Database/Entity/ChangeLog.cs +++ b/DD.Persistence.Database/Entity/ChangeLog.cs @@ -1,8 +1,9 @@  using Microsoft.EntityFrameworkCore; -using DD.Persistence.Models; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using DD.Persistence.ModelsAbstractions; +using DD.Persistence.Database.EntityAbstractions; namespace DD.Persistence.Database.Model; diff --git a/DD.Persistence.Database/Entity/ParameterData.cs b/DD.Persistence.Database/Entity/ParameterData.cs index c81b029..e1e1a5c 100644 --- a/DD.Persistence.Database/Entity/ParameterData.cs +++ b/DD.Persistence.Database/Entity/ParameterData.cs @@ -1,11 +1,12 @@ -using Microsoft.EntityFrameworkCore; +using DD.Persistence.Database.EntityAbstractions; +using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace DD.Persistence.Database.Entity; [PrimaryKey(nameof(DiscriminatorId), nameof(ParameterId), nameof(Timestamp))] -public class ParameterData +public class ParameterData : ITimestampedItem { [Required, Comment("Дискриминатор системы")] public Guid DiscriminatorId { get; set; } diff --git a/DD.Persistence.Database/Entity/Setpoint.cs b/DD.Persistence.Database/Entity/Setpoint.cs index 94eca3f..e3c2ef5 100644 --- a/DD.Persistence.Database/Entity/Setpoint.cs +++ b/DD.Persistence.Database/Entity/Setpoint.cs @@ -1,10 +1,11 @@ -using Microsoft.EntityFrameworkCore; +using DD.Persistence.Database.EntityAbstractions; +using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations.Schema; namespace DD.Persistence.Database.Model { - [PrimaryKey(nameof(Key), nameof(Created))] - public class Setpoint + [PrimaryKey(nameof(Key), nameof(Timestamp))] + public class Setpoint : ITimestampedItem { [Comment("Ключ")] public Guid Key { get; set; } @@ -13,7 +14,7 @@ namespace DD.Persistence.Database.Model public required object Value { get; set; } [Comment("Дата создания уставки")] - public DateTimeOffset Created { get; set; } + public DateTimeOffset Timestamp { get; set; } [Comment("Id автора последнего изменения")] public Guid IdUser { get; set; } diff --git a/DD.Persistence.Database/Entity/TechMessage.cs b/DD.Persistence.Database/Entity/TechMessage.cs index e233941..ef17e60 100644 --- a/DD.Persistence.Database/Entity/TechMessage.cs +++ b/DD.Persistence.Database/Entity/TechMessage.cs @@ -1,10 +1,11 @@ +using DD.Persistence.Database.EntityAbstractions; using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace DD.Persistence.Database.Entity { - public class TechMessage + public class TechMessage : ITimestampedItem { [Key, Comment("Id события")] public Guid EventId { get; set; } diff --git a/DD.Persistence.Database/Entity/TimestampedSet.cs b/DD.Persistence.Database/Entity/TimestampedSet.cs deleted file mode 100644 index f0d1815..0000000 --- a/DD.Persistence.Database/Entity/TimestampedSet.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using System.ComponentModel.DataAnnotations.Schema; - -namespace DD.Persistence.Database.Entity; - -[Comment("Общая таблица данных временных рядов")] -[PrimaryKey(nameof(IdDiscriminator), nameof(Timestamp))] -public record TimestampedSet( - [property: Comment("Дискриминатор ссылка на тип сохраняемых данных")] Guid IdDiscriminator, - [property: Comment("Отметка времени, строго в UTC")] DateTimeOffset Timestamp, - [property: Column(TypeName = "jsonb"), Comment("Набор сохраняемых данных")] IDictionary Set); diff --git a/DD.Persistence.Database/Entity/TagBag.cs b/DD.Persistence.Database/Entity/TimestampedValues.cs similarity index 51% rename from DD.Persistence.Database/Entity/TagBag.cs rename to DD.Persistence.Database/Entity/TimestampedValues.cs index 2aa7546..3205203 100644 --- a/DD.Persistence.Database/Entity/TagBag.cs +++ b/DD.Persistence.Database/Entity/TimestampedValues.cs @@ -1,18 +1,19 @@ -using DD.Persistence.Database.Model; +using DD.Persistence.Database.EntityAbstractions; using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace DD.Persistence.Database.Entity; -[PrimaryKey(nameof(BagId), nameof(Timestamp))] -public class TagBag : ITimestampedData +[PrimaryKey(nameof(DiscriminatorId), nameof(Timestamp))] +public class TimestampedValues : ITimestampedItem { [Comment("Временная отметка"), Key] public DateTimeOffset Timestamp { get; set; } - public Guid BagId { get; set; } + [Comment("Дискриминатор системы"),] + public Guid DiscriminatorId { get; set; } - [Column(TypeName = "jsonb")] + [Comment("Данные"), Column(TypeName = "jsonb")] public required object[] Values { get; set; } } diff --git a/DD.Persistence.Database/Entity/IChangeLog.cs b/DD.Persistence.Database/EntityAbstractions/IChangeLog.cs similarity index 95% rename from DD.Persistence.Database/Entity/IChangeLog.cs rename to DD.Persistence.Database/EntityAbstractions/IChangeLog.cs index b91808b..4d082a7 100644 --- a/DD.Persistence.Database/Entity/IChangeLog.cs +++ b/DD.Persistence.Database/EntityAbstractions/IChangeLog.cs @@ -1,5 +1,4 @@ - -namespace DD.Persistence.Database.Model; +namespace DD.Persistence.Database.EntityAbstractions; /// /// Часть записи, описывающая изменение diff --git a/DD.Persistence.Database/Entity/ITimestampedData.cs b/DD.Persistence.Database/EntityAbstractions/ITimestampedItem.cs similarity index 63% rename from DD.Persistence.Database/Entity/ITimestampedData.cs rename to DD.Persistence.Database/EntityAbstractions/ITimestampedItem.cs index 5a1b25c..f505796 100644 --- a/DD.Persistence.Database/Entity/ITimestampedData.cs +++ b/DD.Persistence.Database/EntityAbstractions/ITimestampedItem.cs @@ -1,5 +1,5 @@ -namespace DD.Persistence.Database.Model; -public interface ITimestampedData +namespace DD.Persistence.Database.EntityAbstractions; +public interface ITimestampedItem { /// /// Дата (должна быть обязательно в UTC) diff --git a/DD.Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs index 4b52f79..481bf57 100644 --- a/DD.Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs @@ -2,8 +2,8 @@ using DD.Persistence.Client; using DD.Persistence.Client.Clients; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; -using DD.Persistence.Database.Model; -using DD.Persistence.Models; +using DD.Persistence.Database.EntityAbstractions; +using DD.Persistence.ModelsAbstractions; using Mapster; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -12,8 +12,8 @@ using Xunit; namespace DD.Persistence.IntegrationTests.Controllers; public abstract class TimeSeriesBaseControllerTest : BaseIntegrationTest - where TEntity : class, ITimestampedData, new() - where TDto : class, ITimeSeriesAbstractDto, new() + where TEntity : class, ITimestampedItem, new() + where TDto : class, ITimestampAbstractDto, new() { private readonly ITimeSeriesClient timeSeriesClient; diff --git a/DD.Persistence.Repository/DependencyInjection.cs b/DD.Persistence.Repository/DependencyInjection.cs index 5114cd0..be9ed55 100644 --- a/DD.Persistence.Repository/DependencyInjection.cs +++ b/DD.Persistence.Repository/DependencyInjection.cs @@ -6,6 +6,7 @@ using DD.Persistence.Repositories; using DD.Persistence.Repository.Repositories; using DD.Persistence.Database.Entity; using System.Reflection; +using DD.Persistence.Repository.RepositoriesCached; namespace DD.Persistence.Repository; public static class DependencyInjection @@ -35,9 +36,9 @@ public static class DependencyInjection MapsterSetup(); - services.AddTransient, TagBagDataRepository>(); + services.AddTransient, TagBagDataRepository>(); services.AddTransient(); - //services.AddTransient, TimeSeriesDataCachedRepository>(); + //services.AddTransient, TimeSeriesDataCachedRepository>(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/DD.Persistence.Repository/QueryBuilders.cs b/DD.Persistence.Repository/QueryBuilders.cs index 6fad2cc..5fb3eb5 100644 --- a/DD.Persistence.Repository/QueryBuilders.cs +++ b/DD.Persistence.Repository/QueryBuilders.cs @@ -1,7 +1,8 @@ using Microsoft.EntityFrameworkCore; -using DD.Persistence.Database.Model; -using DD.Persistence.Models; using DD.Persistence.Models.Requests; +using DD.Persistence.Models.Common; +using DD.Persistence.ModelsAbstractions; +using DD.Persistence.Database.EntityAbstractions; namespace DD.Persistence.Repository; diff --git a/DD.Persistence.Repository/Repositories/ChangeLogRepository.cs b/DD.Persistence.Repository/Repositories/ChangeLogRepository.cs index ef57d10..60fe08e 100644 --- a/DD.Persistence.Repository/Repositories/ChangeLogRepository.cs +++ b/DD.Persistence.Repository/Repositories/ChangeLogRepository.cs @@ -5,6 +5,7 @@ using DD.Persistence.Models; using DD.Persistence.Models.Requests; using DD.Persistence.Repositories; using UuidExtensions; +using DD.Persistence.Models.Common; namespace DD.Persistence.Repository.Repositories; public class ChangeLogRepository : IChangeLogRepository diff --git a/DD.Persistence.Repository/Repositories/ParameterRepository.cs b/DD.Persistence.Repository/Repositories/ParameterRepository.cs index 434d59f..d241de7 100644 --- a/DD.Persistence.Repository/Repositories/ParameterRepository.cs +++ b/DD.Persistence.Repository/Repositories/ParameterRepository.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using DD.Persistence.Database.Entity; using DD.Persistence.Models; using DD.Persistence.Repositories; +using DD.Persistence.Models.Common; namespace DD.Persistence.Repository.Repositories; public class ParameterRepository : IParameterRepository diff --git a/DD.Persistence.Repository/Repositories/SetpointRepository.cs b/DD.Persistence.Repository/Repositories/SetpointRepository.cs index f7a719a..8068b79 100644 --- a/DD.Persistence.Repository/Repositories/SetpointRepository.cs +++ b/DD.Persistence.Repository/Repositories/SetpointRepository.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using DD.Persistence.Database.Model; using DD.Persistence.Models; using DD.Persistence.Repositories; +using DD.Persistence.Models.Common; namespace DD.Persistence.Repository.Repositories { @@ -35,8 +36,8 @@ namespace DD.Persistence.Repository.Repositories .ToArrayAsync(token); var filteredEntities = entities .GroupBy(e => e.Key) - .Select(e => e.OrderBy(o => o.Created)) - .Select(e => e.Where(e => e.Created <= historyMoment).Last()); + .Select(e => e.OrderBy(o => o.Timestamp)) + .Select(e => e.Where(e => e.Timestamp <= historyMoment).Last()); var dtos = filteredEntities .Select(e => e.Adapt()); @@ -47,7 +48,7 @@ namespace DD.Persistence.Repository.Repositories { var query = GetQueryReadOnly(); var entities = await query - .Where(e => e.Created >= dateBegin) + .Where(e => e.Timestamp >= dateBegin) .Take(take) .ToArrayAsync(token); var dtos = entities @@ -62,8 +63,8 @@ namespace DD.Persistence.Repository.Repositories .GroupBy(e => 1) .Select(group => new { - Min = group.Min(e => e.Created), - Max = group.Max(e => e.Created), + Min = group.Min(e => e.Timestamp), + Max = group.Max(e => e.Timestamp), }); var values = await query.FirstOrDefaultAsync(token); var result = new DatesRangeDto() @@ -95,7 +96,7 @@ namespace DD.Persistence.Repository.Repositories Key = setpointKey, Value = newValue, IdUser = idUser, - Created = DateTimeOffset.UtcNow + Timestamp = DateTimeOffset.UtcNow }; await db.Set().AddAsync(entity, token); diff --git a/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs b/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs index 6bbb382..2917021 100644 --- a/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -7,10 +7,11 @@ using DD.Persistence.Models; using DD.Persistence.Models.Requests; using DD.Persistence.Repositories; using UuidExtensions; +using DD.Persistence.Models.Common; namespace DD.Persistence.Repository.Repositories { - public class TechMessagesRepository : ITechMessagesRepository + public class TechMessagesRepository : ITechMessagesRepository { private readonly IDataSourceSystemRepository sourceSystemRepository; private DbContext db; diff --git a/DD.Persistence.Repository/Repositories/TimeSeriesDataCachedRepository.cs b/DD.Persistence.Repository/Repositories/TimeSeriesDataCachedRepository.cs deleted file mode 100644 index 768f747..0000000 --- a/DD.Persistence.Repository/Repositories/TimeSeriesDataCachedRepository.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using DD.Persistence.Database.Model; -using DD.Persistence.Models; - -namespace DD.Persistence.Repository.Repositories; - -public class TimeSeriesDataCachedRepository : TimeSeriesDataRepository - where TEntity : class, ITimestampedData, new() - where TDto : class, ITimeSeriesAbstractDto, new() -{ - public static TDto? FirstByDate { get; private set; } - public static CyclicArray LastData { get; } = new CyclicArray(CacheItemsCount); - - private const int CacheItemsCount = 3600; - - public TimeSeriesDataCachedRepository(DbContext db) : base(db) - { - Task.Run(async () => - { - var firstDateItem = await base.GetFirstAsync(CancellationToken.None); - if (firstDateItem == null) - { - return; - } - - FirstByDate = firstDateItem; - - var dtos = await base.GetLastAsync(CacheItemsCount, CancellationToken.None); - dtos = dtos.OrderBy(d => d.Date); - LastData.AddRange(dtos); - }).Wait(); - } - - public override async Task> GetGtDate(DateTimeOffset dateBegin, CancellationToken token) - { - - if (LastData.Count == 0 || LastData[0].Date > dateBegin) - { - var dtos = await base.GetGtDate(dateBegin, token); - return dtos; - } - - var items = LastData - .Where(i => i.Date >= dateBegin); - - return items; - } - - public override async Task AddRange(IEnumerable dtos, CancellationToken token) - { - var result = await base.AddRange(dtos, token); - if (result > 0) - { - - dtos = dtos.OrderBy(x => x.Date); - - FirstByDate = dtos.First(); - LastData.AddRange(dtos); - } - - return result; - } - - public override async Task GetDatesRange(CancellationToken token) - { - if (FirstByDate == null) - return null; - - return await Task.Run(() => - { - return new DatesRangeDto - { - From = FirstByDate.Date, - To = LastData[^1].Date - }; - }); - } - - public override async Task> GetResampledData( - DateTimeOffset dateBegin, - double intervalSec = 600d, - int approxPointsCount = 1024, - CancellationToken token = default) - { - var dtos = LastData.Where(i => i.Date >= dateBegin); - if (LastData.Count == 0 || LastData[0].Date > dateBegin) - { - dtos = await base.GetGtDate(dateBegin, token); - } - - var dateEnd = dateBegin.AddSeconds(intervalSec); - dtos = dtos - .Where(i => i.Date <= dateEnd); - - var ratio = dtos.Count() / approxPointsCount; - if (ratio > 1) - dtos = dtos - .Where((_, index) => index % ratio == 0); - - return dtos; - } -} - diff --git a/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs index 6f54521..c265d4d 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs @@ -2,6 +2,7 @@ using DD.Persistence.Database.Entity; using DD.Persistence.Models; using DD.Persistence.Repositories; +using DD.Persistence.Models.Common; namespace DD.Persistence.Repository.Repositories; diff --git a/DD.Persistence.Repository/Repositories/TagBagDataRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs similarity index 76% rename from DD.Persistence.Repository/Repositories/TagBagDataRepository.cs rename to DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs index 320b99b..d055594 100644 --- a/DD.Persistence.Repository/Repositories/TagBagDataRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs @@ -1,20 +1,22 @@ -using Mapster; -using Microsoft.EntityFrameworkCore; -using DD.Persistence.Database.Model; -using DD.Persistence.Models; +using DD.Persistence.Database.Entity; +using DD.Persistence.Models.Common; +using DD.Persistence.ModelsAbstractions; using DD.Persistence.Repositories; +using Mapster; +using Microsoft.EntityFrameworkCore; namespace DD.Persistence.Repository.Repositories; -public class TagBagRepository : ITimeSeriesDataRepository +public class TimestampedValuesRepository : ITimestampedValuesRepository + where TDto : class, ITimestampAbstractDto, new() { private readonly DbContext db; - public TagBagRepository(DbContext db) + public TimestampedValuesRepository(DbContext db) { this.db = db; } - protected virtual IQueryable GetQueryReadOnly() => this.db.Set(); + protected virtual IQueryable GetQueryReadOnly() => this.db.Set(); public virtual async Task GetDatesRange(CancellationToken token) { @@ -31,7 +33,7 @@ public class TagBagRepository : ITimeSeriesDataRepository public virtual async Task> GetGtDate(DateTimeOffset date, CancellationToken token) { - var query = this.db.Set().Where(e => e.Timestamp > date); + var query = GetQueryReadOnly().Where(e => e.Timestamp > date); var entities = await query.ToArrayAsync(token); var dtos = entities.Select(e => e.Adapt()); @@ -41,9 +43,9 @@ public class TagBagRepository : ITimeSeriesDataRepository public virtual async Task AddRange(IEnumerable dtos, CancellationToken token) { - var entities = dtos.Select(d => d.Adapt()); + var entities = dtos.Select(d => d.Adapt()); - await db.Set().AddRangeAsync(entities, token); + await db.Set().AddRangeAsync(entities, token); var result = await db.SaveChangesAsync(token); return result; @@ -85,7 +87,7 @@ public class TagBagRepository : ITimeSeriesDataRepository var dateEnd = dateBegin.AddSeconds(intervalSec); dtos = dtos - .Where(i => i.Date <= dateEnd); + .Where(i => i.Timestamp <= dateEnd); var ratio = dtos.Count() / approxPointsCount; if (ratio > 1) diff --git a/DD.Persistence.Repository/Repositories/DataSourceSystemCachedRepository.cs b/DD.Persistence.Repository/RepositoriesCached/DataSourceSystemCachedRepository.cs similarity index 92% rename from DD.Persistence.Repository/Repositories/DataSourceSystemCachedRepository.cs rename to DD.Persistence.Repository/RepositoriesCached/DataSourceSystemCachedRepository.cs index 37009e8..febb65e 100644 --- a/DD.Persistence.Repository/Repositories/DataSourceSystemCachedRepository.cs +++ b/DD.Persistence.Repository/RepositoriesCached/DataSourceSystemCachedRepository.cs @@ -1,8 +1,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using DD.Persistence.Models; +using DD.Persistence.Repository.Repositories; -namespace DD.Persistence.Repository.Repositories; +namespace DD.Persistence.Repository.RepositoriesCached; public class DataSourceSystemCachedRepository : DataSourceSystemRepository { private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DataSourceSystem).FullName}CacheKey"; diff --git a/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs b/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs new file mode 100644 index 0000000..d1eba07 --- /dev/null +++ b/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs @@ -0,0 +1,104 @@ +//using Microsoft.EntityFrameworkCore; +//using DD.Persistence.Models.Common; +//using DD.Persistence.ModelsAbstractions; +//using DD.Persistence.Database.EntityAbstractions; + +//namespace DD.Persistence.Repository.Repositories; + +//public class TimestampedValuesCachedRepository : TimeSeriesDataRepository +// where TEntity : class, ITimestampedItem, new() +// where TDto : class, ITimestampAbstractDto, new() +//{ +// public static TDto? FirstByDate { get; private set; } +// public static CyclicArray LastData { get; } = new CyclicArray(CacheItemsCount); + +// private const int CacheItemsCount = 3600; + +// public TimestampedValuesCachedRepository(DbContext db) : base(db) +// { +// Task.Run(async () => +// { +// var firstDateItem = await base.GetFirstAsync(CancellationToken.None); +// if (firstDateItem == null) +// { +// return; +// } + +// FirstByDate = firstDateItem; + +// var dtos = await base.GetLastAsync(CacheItemsCount, CancellationToken.None); +// dtos = dtos.OrderBy(d => d.Timestamp); +// LastData.AddRange(dtos); +// }).Wait(); +// } + +// public override async Task> GetGtDate(DateTimeOffset dateBegin, CancellationToken token) +// { + +// if (LastData.Count == 0 || LastData[0].Timestamp > dateBegin) +// { +// var dtos = await base.GetGtDate(dateBegin, token); +// return dtos; +// } + +// var items = LastData +// .Where(i => i.Timestamp >= dateBegin); + +// return items; +// } + +// public override async Task AddRange(IEnumerable dtos, CancellationToken token) +// { +// var result = await base.AddRange(dtos, token); +// if (result > 0) +// { + +// dtos = dtos.OrderBy(x => x.Timestamp); + +// FirstByDate = dtos.First(); +// LastData.AddRange(dtos); +// } + +// return result; +// } + +// public override async Task GetDatesRange(CancellationToken token) +// { +// if (FirstByDate == null) +// return null; + +// return await Task.Run(() => +// { +// return new DatesRangeDto +// { +// From = FirstByDate.Timestamp, +// To = LastData[^1].Timestamp +// }; +// }); +// } + +// public override async Task> GetResampledData( +// DateTimeOffset dateBegin, +// double intervalSec = 600d, +// int approxPointsCount = 1024, +// CancellationToken token = default) +// { +// var dtos = LastData.Where(i => i.Timestamp >= dateBegin); +// if (LastData.Count == 0 || LastData[0].Timestamp > dateBegin) +// { +// dtos = await base.GetGtDate(dateBegin, token); +// } + +// var dateEnd = dateBegin.AddSeconds(intervalSec); +// dtos = dtos +// .Where(i => i.Timestamp <= dateEnd); + +// var ratio = dtos.Count() / approxPointsCount; +// if (ratio > 1) +// dtos = dtos +// .Where((_, index) => index % ratio == 0); + +// return dtos; +// } +//} + diff --git a/DD.Persistence/API/ISyncApi.cs b/DD.Persistence/API/ISyncApi.cs index 5bd8379..ec8b9ba 100644 --- a/DD.Persistence/API/ISyncApi.cs +++ b/DD.Persistence/API/ISyncApi.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.API; diff --git a/DD.Persistence/API/ISyncWithDiscriminatorApi.cs b/DD.Persistence/API/ISyncWithDiscriminatorApi.cs index 05f0a90..f57504d 100644 --- a/DD.Persistence/API/ISyncWithDiscriminatorApi.cs +++ b/DD.Persistence/API/ISyncWithDiscriminatorApi.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.API; diff --git a/DD.Persistence/API/ITimeSeriesDataApi.cs b/DD.Persistence/API/ITimeSeriesDataApi.cs index d9b415a..1182455 100644 --- a/DD.Persistence/API/ITimeSeriesDataApi.cs +++ b/DD.Persistence/API/ITimeSeriesDataApi.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Models; +using DD.Persistence.ModelsAbstractions; namespace DD.Persistence.API; @@ -7,7 +7,7 @@ namespace DD.Persistence.API; /// Интерфейс для работы с API временных данных /// public interface ITimeSeriesDataApi : ITimeSeriesBaseDataApi - where TDto : class, ITimeSeriesAbstractDto, new() + where TDto : class, ITimestampAbstractDto, new() { /// /// Получить список объектов, удовлетворяющий диапазон дат diff --git a/DD.Persistence/EFExtensions.cs b/DD.Persistence/EFExtensions.cs index a308db9..af6cd15 100644 --- a/DD.Persistence/EFExtensions.cs +++ b/DD.Persistence/EFExtensions.cs @@ -66,7 +66,7 @@ public static class EFExtensions /// и опционально указания направления сортировки "asc" или "desc" /// /// - /// var query = query("Date desc"); + /// var query = query("Timestamp desc"); /// /// Запрос с примененной сортировкой public static IOrderedQueryable SortBy( diff --git a/DD.Persistence/Models/DatesRangeDto.cs b/DD.Persistence/Models/Common/DatesRangeDto.cs similarity index 89% rename from DD.Persistence/Models/DatesRangeDto.cs rename to DD.Persistence/Models/Common/DatesRangeDto.cs index 0a764ce..2049ed7 100644 --- a/DD.Persistence/Models/DatesRangeDto.cs +++ b/DD.Persistence/Models/Common/DatesRangeDto.cs @@ -1,4 +1,4 @@ -namespace DD.Persistence.Models; +namespace DD.Persistence.Models.Common; /// /// Диапазон дат diff --git a/DD.Persistence/Models/PaginationContainer.cs b/DD.Persistence/Models/Common/PaginationContainer.cs similarity index 95% rename from DD.Persistence/Models/PaginationContainer.cs rename to DD.Persistence/Models/Common/PaginationContainer.cs index 89306f8..ec3aa67 100644 --- a/DD.Persistence/Models/PaginationContainer.cs +++ b/DD.Persistence/Models/Common/PaginationContainer.cs @@ -1,4 +1,4 @@ -namespace DD.Persistence.Models; +namespace DD.Persistence.Models.Common; /// /// Контейнер для поддержки постраничного просмотра таблиц diff --git a/DD.Persistence/Models/DataSaubDto.cs b/DD.Persistence/Models/DataSaubDto.cs deleted file mode 100644 index 34306d9..0000000 --- a/DD.Persistence/Models/DataSaubDto.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace DD.Persistence.Models; -public class DataSaubDto : ITimeSeriesAbstractDto -{ - public DateTimeOffset Date { get; set; } = DateTimeOffset.UtcNow; - - public int? Mode { get; set; } - - public string? User { get; set; } - - public double? WellDepth { get; set; } - - public double? BitDepth { get; set; } - - public double? BlockPosition { get; set; } - - public double? BlockSpeed { get; set; } - - public double? Pressure { get; set; } - - public double? AxialLoad { get; set; } - - public double? HookWeight { get; set; } - - public double? RotorTorque { get; set; } - - public double? RotorSpeed { get; set; } - - public double? Flow { get; set; } - - public short MseState { get; set; } - - public int IdFeedRegulator { get; set; } - - public double? Mse { get; set; } - - public double? Pump0Flow { get; set; } - - public double? Pump1Flow { get; set; } - - public double? Pump2Flow { get; set; } -} diff --git a/DD.Persistence/Models/TimestampedSetDto.cs b/DD.Persistence/Models/TimestampedSetDto.cs index cf72032..20c5cbb 100644 --- a/DD.Persistence/Models/TimestampedSetDto.cs +++ b/DD.Persistence/Models/TimestampedSetDto.cs @@ -1,8 +1,17 @@ namespace DD.Persistence.Models; /// -/// набор данных с отметкой времени +/// Набор данных с отметкой времени /// -/// отметка времени -/// набор данных -public record TimestampedSetDto(DateTimeOffset Timestamp, IDictionary Set); +public class TimestampedSetDto +{ + /// + /// Временная отметка + /// + public DateTimeOffset Timestamp { get; set; } + + /// + /// Набор данных + /// + public required object[] Set { get; set; } +} diff --git a/DD.Persistence/Models/ITimeSeriesAbstractDto.cs b/DD.Persistence/ModelsAbstractions/ITimestampAbstractDto.cs similarity index 59% rename from DD.Persistence/Models/ITimeSeriesAbstractDto.cs rename to DD.Persistence/ModelsAbstractions/ITimestampAbstractDto.cs index b01c104..758e7c5 100644 --- a/DD.Persistence/Models/ITimeSeriesAbstractDto.cs +++ b/DD.Persistence/ModelsAbstractions/ITimestampAbstractDto.cs @@ -1,12 +1,12 @@ -namespace DD.Persistence.Models; +namespace DD.Persistence.ModelsAbstractions; /// /// Интерфейс, описывающий временные данные /// -public interface ITimeSeriesAbstractDto +public interface ITimestampAbstractDto { /// /// временная отметка /// - DateTimeOffset Date { get; set; } + DateTimeOffset Timestamp { get; set; } } diff --git a/DD.Persistence/Models/IWithSectionPart.cs b/DD.Persistence/ModelsAbstractions/IWithSectionPart.cs similarity index 77% rename from DD.Persistence/Models/IWithSectionPart.cs rename to DD.Persistence/ModelsAbstractions/IWithSectionPart.cs index 8b3fbaf..0d3ff1f 100644 --- a/DD.Persistence/Models/IWithSectionPart.cs +++ b/DD.Persistence/ModelsAbstractions/IWithSectionPart.cs @@ -1,4 +1,4 @@ -namespace DD.Persistence.Models; +namespace DD.Persistence.ModelsAbstractions; public interface IWithSectionPart { public double DepthStart { get; set; } diff --git a/DD.Persistence/Repositories/IChangeLogRepository.cs b/DD.Persistence/Repositories/IChangeLogRepository.cs index 41dd379..6c39e24 100644 --- a/DD.Persistence/Repositories/IChangeLogRepository.cs +++ b/DD.Persistence/Repositories/IChangeLogRepository.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; using DD.Persistence.Models.Requests; namespace DD.Persistence.Repositories; diff --git a/DD.Persistence/Repositories/IParameterRepository.cs b/DD.Persistence/Repositories/IParameterRepository.cs index 53c48c3..7e8bbd2 100644 --- a/DD.Persistence/Repositories/IParameterRepository.cs +++ b/DD.Persistence/Repositories/IParameterRepository.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Repositories; public interface IParameterRepository diff --git a/DD.Persistence/Repositories/ISetpointRepository.cs b/DD.Persistence/Repositories/ISetpointRepository.cs index 0af805d..ecce8e0 100644 --- a/DD.Persistence/Repositories/ISetpointRepository.cs +++ b/DD.Persistence/Repositories/ISetpointRepository.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Repositories; diff --git a/DD.Persistence/Repositories/ISyncRepository.cs b/DD.Persistence/Repositories/ISyncRepository.cs index 10c21f2..9a43b01 100644 --- a/DD.Persistence/Repositories/ISyncRepository.cs +++ b/DD.Persistence/Repositories/ISyncRepository.cs @@ -1,4 +1,4 @@ -using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Repositories; diff --git a/DD.Persistence/Repositories/ISyncWithDiscriminatorRepository.cs b/DD.Persistence/Repositories/ISyncWithDiscriminatorRepository.cs index e761c8a..82efb39 100644 --- a/DD.Persistence/Repositories/ISyncWithDiscriminatorRepository.cs +++ b/DD.Persistence/Repositories/ISyncWithDiscriminatorRepository.cs @@ -1,4 +1,4 @@ -using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Repositories; diff --git a/DD.Persistence/Repositories/ITechMessagesRepository.cs b/DD.Persistence/Repositories/ITechMessagesRepository.cs index 9909106..6244485 100644 --- a/DD.Persistence/Repositories/ITechMessagesRepository.cs +++ b/DD.Persistence/Repositories/ITechMessagesRepository.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; using DD.Persistence.Models.Requests; namespace DD.Persistence.Repositories diff --git a/DD.Persistence/Repositories/ITimeSeriesBaseRepository.cs b/DD.Persistence/Repositories/ITimeSeriesBaseRepository.cs index 0d5e531..5f472e0 100644 --- a/DD.Persistence/Repositories/ITimeSeriesBaseRepository.cs +++ b/DD.Persistence/Repositories/ITimeSeriesBaseRepository.cs @@ -1,10 +1,11 @@ -namespace DD.Persistence.Repositories; +using DD.Persistence.Models; + +namespace DD.Persistence.Repositories; /// /// Интерфейс по работе с прореженными данными /// -public interface ITimeSeriesBaseRepository - where TDto : class, new() +public interface ITimeSeriesBaseRepository { /// /// Получить список объектов с прореживанием @@ -12,7 +13,7 @@ public interface ITimeSeriesBaseRepository /// дата начала /// /// - Task> GetResampledData( + Task> GetResampledData( DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, diff --git a/DD.Persistence/Repositories/ITimestampedSetRepository.cs b/DD.Persistence/Repositories/ITimestampedSetRepository.cs index cefbb3e..c448744 100644 --- a/DD.Persistence/Repositories/ITimestampedSetRepository.cs +++ b/DD.Persistence/Repositories/ITimestampedSetRepository.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Repositories; diff --git a/DD.Persistence/Repositories/ITimeSeriesDataRepository.cs b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs similarity index 59% rename from DD.Persistence/Repositories/ITimeSeriesDataRepository.cs rename to DD.Persistence/Repositories/ITimestampedValuesRepository.cs index ed05980..447d197 100644 --- a/DD.Persistence/Repositories/ITimeSeriesDataRepository.cs +++ b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.ModelsAbstractions; namespace DD.Persistence.Repositories; @@ -6,8 +7,8 @@ namespace DD.Persistence.Repositories; /// Интерфейс по работе с временными данными /// /// -public interface ITimeSeriesDataRepository : ISyncRepository, ITimeSeriesBaseRepository - where TDto : class, ITimeSeriesAbstractDto, new() +public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBaseRepository + where TDto : class, ITimestampAbstractDto, new() { /// /// Добавление записей @@ -15,5 +16,5 @@ public interface ITimeSeriesDataRepository : ISyncRepository, ITimeS /// /// /// - Task AddRange(IEnumerable dtos, CancellationToken token); + Task AddRange(IEnumerable dtos, CancellationToken token); } diff --git a/DD.Persistence/Services/Interfaces/IWitsDataService.cs b/DD.Persistence/Services/Interfaces/IWitsDataService.cs index 3a18da9..bb3e7d5 100644 --- a/DD.Persistence/Services/Interfaces/IWitsDataService.cs +++ b/DD.Persistence/Services/Interfaces/IWitsDataService.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.Services.Interfaces; diff --git a/DD.Persistence/Services/WitsDataService.cs b/DD.Persistence/Services/WitsDataService.cs index bcee812..6c6f0ee 100644 --- a/DD.Persistence/Services/WitsDataService.cs +++ b/DD.Persistence/Services/WitsDataService.cs @@ -1,4 +1,5 @@ using DD.Persistence.Models; +using DD.Persistence.Models.Common; using DD.Persistence.Models.Configurations; using DD.Persistence.Models.Enumerations; using DD.Persistence.Repositories; -- 2.45.2 From ce0ad703696843faa5f9a001263f57b0efd40cd1 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Tue, 14 Jan 2025 17:56:59 +0500 Subject: [PATCH 03/16] =?UTF-8?q?A=D0=BA=D1=82=D1=83=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0=D0=BB=20=D0=BF=D0=BE?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B5=20=D1=81=20=D0=B2=D1=80?= =?UTF-8?q?=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D0=BC=D0=B8=20=D1=80=D1=8F?= =?UTF-8?q?=D0=B4=D0=B0=D0=BC=D0=B8=20=D0=BF=D0=BE=D0=B4=20=D0=B5=D0=B4?= =?UTF-8?q?=D0=B8=D0=BD=D1=83=D1=8E=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/DataSaubController.cs | 20 -- .../Controllers/TagBagController.cs | 76 ------- .../Controllers/TimestampedSetController.cs | 69 +++--- DD.Persistence.App/appsettings.Tests.json | 2 +- .../Interfaces/ITimestampedSetClient.cs | 6 +- .../Refit/IRefitTimestampedSetClient.cs | 25 --- .../Refit/IRefitTimestampedValuesClient.cs | 28 +++ .../Clients/TimestampedSetClient.cs | 10 +- DD.Persistence.Client/DependencyInjection.cs | 1 - ...ner.cs => 20250114100429_Init.Designer.cs} | 110 ++-------- ...0062251_Init.cs => 20250114100429_Init.cs} | 47 +--- ...PersistencePostgresContextModelSnapshot.cs | 108 +--------- .../PersistenceDbContext.cs | 8 +- .../Controllers/DataSaubControllerTest.cs | 152 ++++++------- .../TimestampedSetControllerTest.cs | 42 ++-- .../DependencyInjection.cs | 4 +- .../Extensions/IEnumerableExtensions.cs | 17 ++ .../Repositories/TimestampedSetRepository.cs | 200 +++++++++--------- .../TimestampedValuesRepository.cs | 108 +++++++++- ...ampedSetDto.cs => TimestampedValuesDto.cs} | 8 +- .../Repositories/ITimestampedSetRepository.cs | 60 ------ .../ITimestampedValuesRepository.cs | 36 +++- .../ISyncRepository.cs | 11 +- .../ITimeSeriesBaseRepository.cs | 10 +- 24 files changed, 480 insertions(+), 678 deletions(-) delete mode 100644 DD.Persistence.API/Controllers/DataSaubController.cs delete mode 100644 DD.Persistence.API/Controllers/TagBagController.cs delete mode 100644 DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs create mode 100644 DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs rename DD.Persistence.Database.Postgres/Migrations/{20241220062251_Init.Designer.cs => 20250114100429_Init.Designer.cs} (66%) rename DD.Persistence.Database.Postgres/Migrations/{20241220062251_Init.cs => 20250114100429_Init.cs} (71%) create mode 100644 DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs rename DD.Persistence/Models/{TimestampedSetDto.cs => TimestampedValuesDto.cs} (62%) delete mode 100644 DD.Persistence/Repositories/ITimestampedSetRepository.cs rename DD.Persistence/{Repositories => RepositoriesAbstractions}/ISyncRepository.cs (60%) rename DD.Persistence/{Repositories => RepositoriesAbstractions}/ITimeSeriesBaseRepository.cs (65%) diff --git a/DD.Persistence.API/Controllers/DataSaubController.cs b/DD.Persistence.API/Controllers/DataSaubController.cs deleted file mode 100644 index df86844..0000000 --- a/DD.Persistence.API/Controllers/DataSaubController.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Models; -using DD.Persistence.Repositories; - -namespace DD.Persistence.API.Controllers; - -/// -/// Работа с временными данными -/// -[ApiController] -[Authorize] -[Route("api/[controller]")] -public class DataSaubController : TimeSeriesController -{ - public DataSaubController(ITimestampedValuesRepository timeSeriesDataRepository) : base(timeSeriesDataRepository) - { - - } -} diff --git a/DD.Persistence.API/Controllers/TagBagController.cs b/DD.Persistence.API/Controllers/TagBagController.cs deleted file mode 100644 index c7b8475..0000000 --- a/DD.Persistence.API/Controllers/TagBagController.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Repositories; -using DD.Persistence.ModelsAbstractions; - -namespace DD.Persistence.API.Controllers; - -[ApiController] -[Authorize] -[Route("api/[controller]")] -public class TimeSeriesController : ControllerBase, ITimeSeriesDataApi - where TDto : class, ITimestampAbstractDto, new() -{ - private readonly ITimestampedValuesRepository timeSeriesDataRepository; - - public TimeSeriesController(ITimestampedValuesRepository timeSeriesDataRepository) - { - this.timeSeriesDataRepository = timeSeriesDataRepository; - } - - /// - /// Получить список объектов, удовлетворяющий диапазону дат - /// - /// - /// - /// - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task Get(DateTimeOffset dateBegin, CancellationToken token) - { - var result = await timeSeriesDataRepository.GetGtDate(dateBegin, token); - return Ok(result); - } - - /// - /// Получить диапазон дат, для которых есть данные в репозитории - /// - /// - /// - [HttpGet("datesRange")] - public async Task GetDatesRange(CancellationToken token) - { - var result = await timeSeriesDataRepository.GetDatesRange(token); - return Ok(result); - } - - /// - /// Получить список объектов с прореживанием, удовлетворяющий диапазону дат - /// - /// - /// - /// - /// - /// - [HttpGet("resampled")] - public async Task GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) - { - var result = await timeSeriesDataRepository.GetResampledData(dateBegin, intervalSec, approxPointsCount, token); - return Ok(result); - } - - /// - /// Добавить записи - /// - /// - /// - /// - [HttpPost] - public async Task AddRange(IEnumerable dtos, CancellationToken token) - { - var result = await timeSeriesDataRepository.AddRange(dtos, token); - return Ok(result); - } - - -} diff --git a/DD.Persistence.API/Controllers/TimestampedSetController.cs b/DD.Persistence.API/Controllers/TimestampedSetController.cs index c4514f5..878ee3d 100644 --- a/DD.Persistence.API/Controllers/TimestampedSetController.cs +++ b/DD.Persistence.API/Controllers/TimestampedSetController.cs @@ -1,9 +1,8 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Models; -using DD.Persistence.Repositories; -using System.Net; +using DD.Persistence.Models; using DD.Persistence.Models.Common; +using DD.Persistence.Repositories; +using Microsoft.AspNetCore.Mvc; +using System.Net; namespace DD.Persistence.API.Controllers; @@ -12,13 +11,13 @@ namespace DD.Persistence.API.Controllers; /// Не оптимизировано под большие данные. /// [ApiController] -[Authorize] -[Route("api/[controller]/{idDiscriminator}")] +//[Authorize] +[Route("api/[controller]/{discriminatorId}")] public class TimestampedSetController : ControllerBase { - private readonly ITimestampedSetRepository repository; + private readonly ITimestampedValuesRepository repository; - public TimestampedSetController(ITimestampedSetRepository repository) + public TimestampedSetController(ITimestampedValuesRepository repository) { this.repository = repository; } @@ -27,22 +26,22 @@ public class TimestampedSetController : ControllerBase /// Записать новые данные /// Предполагается что данные с одним дискриминатором имеют одинаковую структуру /// - /// Дискриминатор (идентификатор) набора + /// Дискриминатор (идентификатор) набора /// /// /// кол-во затронутых записей [HttpPost] [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - public async Task AddRange([FromRoute] Guid idDiscriminator, [FromBody] IEnumerable sets, CancellationToken token) + public async Task AddRange([FromRoute] Guid discriminatorId, [FromBody] IEnumerable sets, CancellationToken token) { - var result = await repository.AddRange(idDiscriminator, sets, token); + var result = await repository.AddRange(discriminatorId, sets, token); return Ok(result); } /// /// Получение данных с фильтрацией. Значение фильтра null - отключен /// - /// Дискриминатор (идентификатор) набора + /// Дискриминатор (идентификатор) набора /// Фильтр позднее даты /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора /// @@ -50,56 +49,74 @@ public class TimestampedSetController : ControllerBase /// /// Фильтрованный набор данных с сортировкой по отметке времени [HttpGet] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, [FromQuery] IEnumerable? columnNames, int skip, int take, CancellationToken token) + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + public async Task Get([FromRoute] Guid discriminatorId, DateTimeOffset? geTimestamp, [FromQuery] IEnumerable? columnNames, int skip, int take, CancellationToken token) { - var result = await repository.Get(idDiscriminator, geTimestamp, columnNames, skip, take, token); + var result = await repository.Get(discriminatorId, geTimestamp, columnNames, skip, take, token); return Ok(result); } /// /// Получить последние данные /// - /// Дискриминатор (идентификатор) набора + /// Дискриминатор (идентификатор) набора /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора /// /// /// Фильтрованный набор данных с сортировкой по отметке времени [HttpGet("last")] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task GetLast(Guid idDiscriminator, [FromQuery] IEnumerable? columnNames, int take, CancellationToken token) + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + public async Task GetLast([FromRoute] Guid discriminatorId, [FromQuery] IEnumerable? columnNames, int take, CancellationToken token) { - var result = await repository.GetLast(idDiscriminator, columnNames, take, token); + var result = await repository.GetLast(discriminatorId, columnNames, take, token); return Ok(result); } /// /// Диапазон дат за которые есть данные /// - /// + /// /// /// Дата первой и последней записи [HttpGet("datesRange")] [ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] - public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) + public async Task GetDatesRange([FromRoute] Guid discriminatorId, CancellationToken token) { - var result = await repository.GetDatesRange(idDiscriminator, token); + var result = await repository.GetDatesRange(discriminatorId, token); return Ok(result); } /// /// Количество записей по указанному набору в БД. Для пагинации. /// - /// Дискриминатор (идентификатор) набора + /// Дискриминатор (идентификатор) набора /// /// [HttpGet("count")] [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] - public async Task Count(Guid idDiscriminator, CancellationToken token) + public async Task Count([FromRoute] Guid discriminatorId, CancellationToken token) { - var result = await repository.Count(idDiscriminator, token); + var result = await repository.Count(discriminatorId, token); + return Ok(result); + } + + /// + /// Получить список объектов с прореживанием, удовлетворяющий диапазону дат + /// + /// + /// + /// + /// + /// + /// + [HttpGet("resampled")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public async Task GetResampledData([FromRoute] Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) + { + var result = await repository.GetResampledData(discriminatorId, dateBegin, intervalSec, approxPointsCount, token); return Ok(result); } } diff --git a/DD.Persistence.App/appsettings.Tests.json b/DD.Persistence.App/appsettings.Tests.json index 72c43d3..9934757 100644 --- a/DD.Persistence.App/appsettings.Tests.json +++ b/DD.Persistence.App/appsettings.Tests.json @@ -1,6 +1,6 @@ { "DbConnection": { - "Host": "postgres", + "Host": "localhost", "Port": 5432, "Database": "persistence", "Username": "postgres", diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs index 5efd6e7..701beb9 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs @@ -18,7 +18,7 @@ public interface ITimestampedSetClient : IDisposable /// /// /// - Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); + Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); /// /// Количество записей по указанному набору в БД. Для пагинации @@ -38,7 +38,7 @@ public interface ITimestampedSetClient : IDisposable /// /// /// - Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); + Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); /// /// Диапазон дат за которые есть данные @@ -56,5 +56,5 @@ public interface ITimestampedSetClient : IDisposable /// /// /// - Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token); + Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token); } \ No newline at end of file diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs deleted file mode 100644 index 921e867..0000000 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs +++ /dev/null @@ -1,25 +0,0 @@ -using DD.Persistence.Models; -using DD.Persistence.Models.Common; -using Refit; - -namespace DD.Persistence.Client.Clients.Interfaces.Refit; - -public interface IRefitTimestampedSetClient : IRefitClient, IDisposable -{ - private const string baseUrl = "/api/TimestampedSet/{idDiscriminator}"; - - [Post(baseUrl)] - Task> AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); - - [Get(baseUrl)] - Task>> Get(Guid idDiscriminator, [Query] DateTimeOffset? geTimestamp, [Query] IEnumerable? columnNames, int skip, int take, CancellationToken token); - - [Get($"{baseUrl}/last")] - Task>> GetLast(Guid idDiscriminator, [Query] IEnumerable? columnNames, int take, CancellationToken token); - - [Get($"{baseUrl}/count")] - Task> Count(Guid idDiscriminator, CancellationToken token); - - [Get($"{baseUrl}/datesRange")] - Task> GetDatesRange(Guid idDiscriminator, CancellationToken token); -} diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs new file mode 100644 index 0000000..bf7a386 --- /dev/null +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs @@ -0,0 +1,28 @@ +using DD.Persistence.Models; +using DD.Persistence.Models.Common; +using Refit; + +namespace DD.Persistence.Client.Clients.Interfaces.Refit; + +public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable +{ + private const string baseUrl = "/api/TimestampedSet/{idDiscriminator}"; + + [Post(baseUrl)] + Task> AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); + + [Get(baseUrl)] + Task>> Get(Guid idDiscriminator, [Query] DateTimeOffset? geTimestamp, [Query] IEnumerable? columnNames, int skip, int take, CancellationToken token); + + [Get($"{baseUrl}/last")] + Task>> GetLast(Guid discriminatorId, [Query] IEnumerable? columnNames, int take, CancellationToken token); + + [Get($"{baseUrl}/count")] + Task> Count(Guid discriminatorId, CancellationToken token); + + [Get($"{baseUrl}/datesRange")] + Task> GetDatesRange(Guid discriminatorId, CancellationToken token); + + [Get($"{baseUrl}/resampled")] + Task>> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); +} diff --git a/DD.Persistence.Client/Clients/TimestampedSetClient.cs b/DD.Persistence.Client/Clients/TimestampedSetClient.cs index d9f71dd..14fdee9 100644 --- a/DD.Persistence.Client/Clients/TimestampedSetClient.cs +++ b/DD.Persistence.Client/Clients/TimestampedSetClient.cs @@ -8,14 +8,14 @@ using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients; public class TimestampedSetClient : BaseClient, ITimestampedSetClient { - private readonly IRefitTimestampedSetClient refitTimestampedSetClient; + private readonly IRefitTimestampedValuesClient refitTimestampedSetClient; - public TimestampedSetClient(IRefitClientFactory refitTimestampedSetClientFactory, ILogger logger) : base(logger) + public TimestampedSetClient(IRefitClientFactory refitTimestampedSetClientFactory, ILogger logger) : base(logger) { this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create(); } - public async Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token) + public async Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token) { var result = await ExecutePostResponse( async () => await refitTimestampedSetClient.AddRange(idDiscriminator, sets, token), token); @@ -23,7 +23,7 @@ public class TimestampedSetClient : BaseClient, ITimestampedSetClient return result; } - public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) { var result = await ExecuteGetResponse( async () => await refitTimestampedSetClient.Get(idDiscriminator, geTimestamp, columnNames, skip, take, token), token); @@ -31,7 +31,7 @@ public class TimestampedSetClient : BaseClient, ITimestampedSetClient return result!; } - public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) + public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) { var result = await ExecuteGetResponse( async () => await refitTimestampedSetClient.GetLast(idDiscriminator, columnNames, take, token), token); diff --git a/DD.Persistence.Client/DependencyInjection.cs b/DD.Persistence.Client/DependencyInjection.cs index eced892..43a8733 100644 --- a/DD.Persistence.Client/DependencyInjection.cs +++ b/DD.Persistence.Client/DependencyInjection.cs @@ -22,7 +22,6 @@ public static class DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient, TimeSeriesClient>(); services.AddTransient(); services.AddTransient(); return services; diff --git a/DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.Designer.cs b/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.Designer.cs similarity index 66% rename from DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.Designer.cs rename to DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.Designer.cs index 02a25ce..8d0173d 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.Designer.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.Designer.cs @@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace DD.Persistence.Database.Postgres.Migrations { [DbContext(typeof(PersistencePostgresContext))] - [Migration("20241220062251_Init")] + [Migration("20250114100429_Init")] partial class Init { /// @@ -20,7 +20,7 @@ namespace DD.Persistence.Database.Postgres.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("ProductVersion", "9.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -105,27 +105,24 @@ namespace DD.Persistence.Database.Postgres.Migrations b.ToTable("TechMessage"); }); - modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedSet", b => + modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b => { - b.Property("IdDiscriminator") + b.Property("DiscriminatorId") .HasColumnType("uuid") - .HasComment("Дискриминатор ссылка на тип сохраняемых данных"); + .HasComment("Дискриминатор системы"); b.Property("Timestamp") .HasColumnType("timestamp with time zone") - .HasComment("Отметка времени, строго в UTC"); + .HasComment("Временная отметка"); - b.Property("Set") + b.Property("Values") .IsRequired() .HasColumnType("jsonb") - .HasComment("Набор сохраняемых данных"); + .HasComment("Данные"); - b.HasKey("IdDiscriminator", "Timestamp"); + b.HasKey("DiscriminatorId", "Timestamp"); - b.ToTable("TimestampedSets", t => - { - t.HasComment("Общая таблица данных временных рядов"); - }); + b.ToTable("TimestampedSets"); }); modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b => @@ -181,96 +178,13 @@ namespace DD.Persistence.Database.Postgres.Migrations b.ToTable("ChangeLog"); }); - modelBuilder.Entity("DD.Persistence.Database.Model.DataSaub", b => - { - b.Property("Date") - .HasColumnType("timestamp with time zone") - .HasColumnName("date"); - - b.Property("AxialLoad") - .HasColumnType("double precision") - .HasColumnName("axialLoad"); - - b.Property("BitDepth") - .HasColumnType("double precision") - .HasColumnName("bitDepth"); - - b.Property("BlockPosition") - .HasColumnType("double precision") - .HasColumnName("blockPosition"); - - b.Property("BlockSpeed") - .HasColumnType("double precision") - .HasColumnName("blockSpeed"); - - b.Property("Flow") - .HasColumnType("double precision") - .HasColumnName("flow"); - - b.Property("HookWeight") - .HasColumnType("double precision") - .HasColumnName("hookWeight"); - - b.Property("IdFeedRegulator") - .HasColumnType("integer") - .HasColumnName("idFeedRegulator"); - - b.Property("Mode") - .HasColumnType("integer") - .HasColumnName("mode"); - - b.Property("Mse") - .HasColumnType("double precision") - .HasColumnName("mse"); - - b.Property("MseState") - .HasColumnType("smallint") - .HasColumnName("mseState"); - - b.Property("Pressure") - .HasColumnType("double precision") - .HasColumnName("pressure"); - - b.Property("Pump0Flow") - .HasColumnType("double precision") - .HasColumnName("pump0Flow"); - - b.Property("Pump1Flow") - .HasColumnType("double precision") - .HasColumnName("pump1Flow"); - - b.Property("Pump2Flow") - .HasColumnType("double precision") - .HasColumnName("pump2Flow"); - - b.Property("RotorSpeed") - .HasColumnType("double precision") - .HasColumnName("rotorSpeed"); - - b.Property("RotorTorque") - .HasColumnType("double precision") - .HasColumnName("rotorTorque"); - - b.Property("User") - .HasColumnType("text") - .HasColumnName("user"); - - b.Property("WellDepth") - .HasColumnType("double precision") - .HasColumnName("wellDepth"); - - b.HasKey("Date"); - - b.ToTable("DataSaub"); - }); - modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b => { b.Property("Key") .HasColumnType("uuid") .HasComment("Ключ"); - b.Property("Created") + b.Property("Timestamp") .HasColumnType("timestamp with time zone") .HasComment("Дата создания уставки"); @@ -283,7 +197,7 @@ namespace DD.Persistence.Database.Postgres.Migrations .HasColumnType("jsonb") .HasComment("Значение уставки"); - b.HasKey("Key", "Created"); + b.HasKey("Key", "Timestamp"); b.ToTable("Setpoint"); }); diff --git a/DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.cs b/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.cs similarity index 71% rename from DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.cs rename to DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.cs index 03a8c43..999a231 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.cs @@ -32,35 +32,6 @@ namespace DD.Persistence.Database.Postgres.Migrations table.PrimaryKey("PK_ChangeLog", x => x.Id); }); - migrationBuilder.CreateTable( - name: "DataSaub", - columns: table => new - { - date = table.Column(type: "timestamp with time zone", nullable: false), - mode = table.Column(type: "integer", nullable: true), - user = table.Column(type: "text", nullable: true), - wellDepth = table.Column(type: "double precision", nullable: true), - bitDepth = table.Column(type: "double precision", nullable: true), - blockPosition = table.Column(type: "double precision", nullable: true), - blockSpeed = table.Column(type: "double precision", nullable: true), - pressure = table.Column(type: "double precision", nullable: true), - axialLoad = table.Column(type: "double precision", nullable: true), - hookWeight = table.Column(type: "double precision", nullable: true), - rotorTorque = table.Column(type: "double precision", nullable: true), - rotorSpeed = table.Column(type: "double precision", nullable: true), - flow = table.Column(type: "double precision", nullable: true), - mseState = table.Column(type: "smallint", nullable: false), - idFeedRegulator = table.Column(type: "integer", nullable: false), - mse = table.Column(type: "double precision", nullable: true), - pump0Flow = table.Column(type: "double precision", nullable: true), - pump1Flow = table.Column(type: "double precision", nullable: true), - pump2Flow = table.Column(type: "double precision", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_DataSaub", x => x.date); - }); - migrationBuilder.CreateTable( name: "DataSourceSystem", columns: table => new @@ -93,28 +64,27 @@ namespace DD.Persistence.Database.Postgres.Migrations columns: table => new { Key = table.Column(type: "uuid", nullable: false, comment: "Ключ"), - Created = table.Column(type: "timestamp with time zone", nullable: false, comment: "Дата создания уставки"), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Дата создания уставки"), Value = table.Column(type: "jsonb", nullable: false, comment: "Значение уставки"), IdUser = table.Column(type: "uuid", nullable: false, comment: "Id автора последнего изменения") }, constraints: table => { - table.PrimaryKey("PK_Setpoint", x => new { x.Key, x.Created }); + table.PrimaryKey("PK_Setpoint", x => new { x.Key, x.Timestamp }); }); migrationBuilder.CreateTable( name: "TimestampedSets", columns: table => new { - IdDiscriminator = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор ссылка на тип сохраняемых данных"), - Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Отметка времени, строго в UTC"), - Set = table.Column(type: "jsonb", nullable: false, comment: "Набор сохраняемых данных") + Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Временная отметка"), + DiscriminatorId = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор системы"), + Values = table.Column(type: "jsonb", nullable: false, comment: "Данные") }, constraints: table => { - table.PrimaryKey("PK_TimestampedSets", x => new { x.IdDiscriminator, x.Timestamp }); - }, - comment: "Общая таблица данных временных рядов"); + table.PrimaryKey("PK_TimestampedSets", x => new { x.DiscriminatorId, x.Timestamp }); + }); migrationBuilder.CreateTable( name: "TechMessage", @@ -150,9 +120,6 @@ namespace DD.Persistence.Database.Postgres.Migrations migrationBuilder.DropTable( name: "ChangeLog"); - migrationBuilder.DropTable( - name: "DataSaub"); - migrationBuilder.DropTable( name: "ParameterData"); diff --git a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs index 4c66e90..6e10f12 100644 --- a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs +++ b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace DD.Persistence.Database.Postgres.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("ProductVersion", "9.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -102,27 +102,24 @@ namespace DD.Persistence.Database.Postgres.Migrations b.ToTable("TechMessage"); }); - modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedSet", b => + modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b => { - b.Property("IdDiscriminator") + b.Property("DiscriminatorId") .HasColumnType("uuid") - .HasComment("Дискриминатор ссылка на тип сохраняемых данных"); + .HasComment("Дискриминатор системы"); b.Property("Timestamp") .HasColumnType("timestamp with time zone") - .HasComment("Отметка времени, строго в UTC"); + .HasComment("Временная отметка"); - b.Property("Set") + b.Property("Values") .IsRequired() .HasColumnType("jsonb") - .HasComment("Набор сохраняемых данных"); + .HasComment("Данные"); - b.HasKey("IdDiscriminator", "Timestamp"); + b.HasKey("DiscriminatorId", "Timestamp"); - b.ToTable("TimestampedSets", t => - { - t.HasComment("Общая таблица данных временных рядов"); - }); + b.ToTable("TimestampedSets"); }); modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b => @@ -178,96 +175,13 @@ namespace DD.Persistence.Database.Postgres.Migrations b.ToTable("ChangeLog"); }); - modelBuilder.Entity("DD.Persistence.Database.Model.DataSaub", b => - { - b.Property("Date") - .HasColumnType("timestamp with time zone") - .HasColumnName("date"); - - b.Property("AxialLoad") - .HasColumnType("double precision") - .HasColumnName("axialLoad"); - - b.Property("BitDepth") - .HasColumnType("double precision") - .HasColumnName("bitDepth"); - - b.Property("BlockPosition") - .HasColumnType("double precision") - .HasColumnName("blockPosition"); - - b.Property("BlockSpeed") - .HasColumnType("double precision") - .HasColumnName("blockSpeed"); - - b.Property("Flow") - .HasColumnType("double precision") - .HasColumnName("flow"); - - b.Property("HookWeight") - .HasColumnType("double precision") - .HasColumnName("hookWeight"); - - b.Property("IdFeedRegulator") - .HasColumnType("integer") - .HasColumnName("idFeedRegulator"); - - b.Property("Mode") - .HasColumnType("integer") - .HasColumnName("mode"); - - b.Property("Mse") - .HasColumnType("double precision") - .HasColumnName("mse"); - - b.Property("MseState") - .HasColumnType("smallint") - .HasColumnName("mseState"); - - b.Property("Pressure") - .HasColumnType("double precision") - .HasColumnName("pressure"); - - b.Property("Pump0Flow") - .HasColumnType("double precision") - .HasColumnName("pump0Flow"); - - b.Property("Pump1Flow") - .HasColumnType("double precision") - .HasColumnName("pump1Flow"); - - b.Property("Pump2Flow") - .HasColumnType("double precision") - .HasColumnName("pump2Flow"); - - b.Property("RotorSpeed") - .HasColumnType("double precision") - .HasColumnName("rotorSpeed"); - - b.Property("RotorTorque") - .HasColumnType("double precision") - .HasColumnName("rotorTorque"); - - b.Property("User") - .HasColumnType("text") - .HasColumnName("user"); - - b.Property("WellDepth") - .HasColumnType("double precision") - .HasColumnName("wellDepth"); - - b.HasKey("Date"); - - b.ToTable("DataSaub"); - }); - modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b => { b.Property("Key") .HasColumnType("uuid") .HasComment("Ключ"); - b.Property("Created") + b.Property("Timestamp") .HasColumnType("timestamp with time zone") .HasComment("Дата создания уставки"); @@ -280,7 +194,7 @@ namespace DD.Persistence.Database.Postgres.Migrations .HasColumnType("jsonb") .HasComment("Значение уставки"); - b.HasKey("Key", "Created"); + b.HasKey("Key", "Timestamp"); b.ToTable("Setpoint"); }); diff --git a/DD.Persistence.Database/PersistenceDbContext.cs b/DD.Persistence.Database/PersistenceDbContext.cs index a0587cd..0cddcba 100644 --- a/DD.Persistence.Database/PersistenceDbContext.cs +++ b/DD.Persistence.Database/PersistenceDbContext.cs @@ -9,11 +9,9 @@ namespace DD.Persistence.Database; /// public class PersistenceDbContext : DbContext { - public DbSet DataSaub => Set(); - public DbSet Setpoint => Set(); - public DbSet TimestampedSets => Set(); + public DbSet TimestampedSets => Set(); public DbSet ChangeLog => Set(); @@ -31,8 +29,8 @@ public class PersistenceDbContext : DbContext protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity() - .Property(e => e.Set) + modelBuilder.Entity() + .Property(e => e.Values) .HasJsonConversion(); modelBuilder.Entity() diff --git a/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs index 3429651..bb82675 100644 --- a/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs @@ -1,85 +1,85 @@ -using DD.Persistence.Database.Model; -using DD.Persistence.Models; -using Xunit; +//using DD.Persistence.Database.Model; +//using DD.Persistence.Models; +//using Xunit; -namespace DD.Persistence.IntegrationTests.Controllers; -public class DataSaubControllerTest : TimeSeriesBaseControllerTest -{ - private readonly DataSaubDto dto = new() - { - AxialLoad = 1, - BitDepth = 2, - BlockPosition = 3, - BlockSpeed = 4, - Date = DateTimeOffset.UtcNow, - Flow = 5, - HookWeight = 6, - IdFeedRegulator = 8, - Mode = 9, - Mse = 10, - MseState = 11, - Pressure = 12, - Pump0Flow = 13, - Pump1Flow = 14, - Pump2Flow = 15, - RotorSpeed = 16, - RotorTorque = 17, - User = string.Empty, - WellDepth = 18, - }; +//namespace DD.Persistence.IntegrationTests.Controllers; +//public class DataSaubControllerTest : TimeSeriesBaseControllerTest +//{ +// private readonly DataSaubDto dto = new() +// { +// AxialLoad = 1, +// BitDepth = 2, +// BlockPosition = 3, +// BlockSpeed = 4, +// Date = DateTimeOffset.UtcNow, +// Flow = 5, +// HookWeight = 6, +// IdFeedRegulator = 8, +// Mode = 9, +// Mse = 10, +// MseState = 11, +// Pressure = 12, +// Pump0Flow = 13, +// Pump1Flow = 14, +// Pump2Flow = 15, +// RotorSpeed = 16, +// RotorTorque = 17, +// User = string.Empty, +// WellDepth = 18, +// }; - private readonly DataSaub entity = new() - { - AxialLoad = 1, - BitDepth = 2, - BlockPosition = 3, - BlockSpeed = 4, - Timestamp = DateTimeOffset.UtcNow, - Flow = 5, - HookWeight = 6, - IdFeedRegulator = 8, - Mode = 9, - Mse = 10, - MseState = 11, - Pressure = 12, - Pump0Flow = 13, - Pump1Flow = 14, - Pump2Flow = 15, - RotorSpeed = 16, - RotorTorque = 17, - User = string.Empty, - WellDepth = 18, - }; +// private readonly DataSaub entity = new() +// { +// AxialLoad = 1, +// BitDepth = 2, +// BlockPosition = 3, +// BlockSpeed = 4, +// Timestamp = DateTimeOffset.UtcNow, +// Flow = 5, +// HookWeight = 6, +// IdFeedRegulator = 8, +// Mode = 9, +// Mse = 10, +// MseState = 11, +// Pressure = 12, +// Pump0Flow = 13, +// Pump1Flow = 14, +// Pump2Flow = 15, +// RotorSpeed = 16, +// RotorTorque = 17, +// User = string.Empty, +// WellDepth = 18, +// }; - public DataSaubControllerTest(WebAppFactoryFixture factory) : base(factory) - { - } +// public DataSaubControllerTest(WebAppFactoryFixture factory) : base(factory) +// { +// } - [Fact] - public async Task InsertRange_returns_success() - { - await InsertRangeSuccess(dto); - } +// [Fact] +// public async Task InsertRange_returns_success() +// { +// await InsertRangeSuccess(dto); +// } - [Fact] - public async Task Get_returns_success() - { - var beginDate = DateTimeOffset.UtcNow.AddDays(-1); - var endDate = DateTimeOffset.UtcNow; - await GetSuccess(beginDate, endDate, entity); - } +// [Fact] +// public async Task Get_returns_success() +// { +// var beginDate = DateTimeOffset.UtcNow.AddDays(-1); +// var endDate = DateTimeOffset.UtcNow; +// await GetSuccess(beginDate, endDate, entity); +// } - [Fact] - public async Task GetDatesRange_returns_success() - { - await GetDatesRangeSuccess(entity); - } +// [Fact] +// public async Task GetDatesRange_returns_success() +// { +// await GetDatesRangeSuccess(entity); +// } - [Fact] - public async Task GetResampledData_returns_success() - { - await GetResampledDataSuccess(entity); - } -} +// [Fact] +// public async Task GetResampledData_returns_success() +// { +// await GetResampledDataSuccess(entity); +// } +//} diff --git a/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs index 1e84e9d..c929230 100644 --- a/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs @@ -15,7 +15,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest public TimestampedSetControllerTest(WebAppFactoryFixture factory) : base(factory) { var refitClientFactory = scope.ServiceProvider - .GetRequiredService>(); + .GetRequiredService>(); var logger = scope.ServiceProvider.GetRequiredService>(); client = scope.ServiceProvider @@ -27,7 +27,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest { // arrange Guid idDiscriminator = Guid.NewGuid(); - IEnumerable testSets = Generate(10, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(10, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); // act var response = await client.AddRange(idDiscriminator, testSets, CancellationToken.None); @@ -42,7 +42,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest // arrange Guid idDiscriminator = Guid.NewGuid(); int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); // act @@ -59,7 +59,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest // arrange Guid idDiscriminator = Guid.NewGuid(); int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); string[] props = ["A"]; @@ -71,9 +71,9 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Assert.Equal(count, response.Count()); foreach (var item in response) { - Assert.Single(item.Set); - var kv = item.Set.First(); - Assert.Equal("A", kv.Key); + Assert.Single(item.Values); + var kv = item.Values.First(); + Assert.Equal("A", ((KeyValuePair) kv).Key); } } @@ -85,7 +85,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest int count = 10; var dateMin = DateTimeOffset.Now; var dateMax = DateTimeOffset.Now.AddSeconds(count); - IEnumerable testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); var insertResponse = await client.AddRange(idDiscriminator, testSets, CancellationToken.None); var tail = testSets.OrderBy(t => t.Timestamp).Skip(count / 2).Take(int.MaxValue); var geDate = tail.First().Timestamp; @@ -108,7 +108,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest // arrange Guid idDiscriminator = Guid.NewGuid(); int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); var expectedCount = count / 2; @@ -128,7 +128,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Guid idDiscriminator = Guid.NewGuid(); var expectedCount = 1; int count = 10 + expectedCount; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); // act @@ -145,7 +145,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest // arrange Guid idDiscriminator = Guid.NewGuid(); int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); var expectedCount = 8; @@ -165,7 +165,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest int count = 10; var dateMin = DateTimeOffset.Now; var dateMax = DateTimeOffset.Now.AddSeconds(count - 1); - IEnumerable testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); var tolerance = TimeSpan.FromSeconds(1); @@ -184,7 +184,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest // arrange Guid idDiscriminator = Guid.NewGuid(); int count = 144; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); // act @@ -194,18 +194,20 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Assert.Equal(count, response); } - private static IEnumerable Generate(int n, DateTimeOffset from) + private static IEnumerable Generate(int n, DateTimeOffset from) { for (int i = 0; i < n; i++) - yield return new TimestampedSetDto - ( - from.AddSeconds(i), - new Dictionary{ + yield return new TimestampedValuesDto() + { + Timestamp = from.AddSeconds(i), + Values = new Dictionary { {"A", i }, {"B", i * 1.1 }, {"C", $"Any{i}" }, - {"D", DateTimeOffset.Now}, + {"D", DateTimeOffset.Now} } - ); + .Select(e => (object) e) + .ToArray() + }; } } diff --git a/DD.Persistence.Repository/DependencyInjection.cs b/DD.Persistence.Repository/DependencyInjection.cs index be9ed55..492c502 100644 --- a/DD.Persistence.Repository/DependencyInjection.cs +++ b/DD.Persistence.Repository/DependencyInjection.cs @@ -36,11 +36,9 @@ public static class DependencyInjection MapsterSetup(); - services.AddTransient, TagBagDataRepository>(); services.AddTransient(); - //services.AddTransient, TimeSeriesDataCachedRepository>(); services.AddTransient(); - services.AddTransient(); + services.AddTransient, TimestampedValuesRepository>(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs b/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs new file mode 100644 index 0000000..270107f --- /dev/null +++ b/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs @@ -0,0 +1,17 @@ +namespace DD.Persistence.Repository.Extensions; + +public static class IEnumerableExtensions +{ + public static void ForEach(this IEnumerable source, Action action) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (action == null) + throw new ArgumentNullException(nameof(action)); + + foreach (var item in source) + { + action(item); + } + } +} diff --git a/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs index c265d4d..73225be 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs @@ -1,122 +1,122 @@ -using Microsoft.EntityFrameworkCore; -using DD.Persistence.Database.Entity; -using DD.Persistence.Models; -using DD.Persistence.Repositories; -using DD.Persistence.Models.Common; +//using Microsoft.EntityFrameworkCore; +//using DD.Persistence.Database.Entity; +//using DD.Persistence.Models; +//using DD.Persistence.Repositories; +//using DD.Persistence.Models.Common; -namespace DD.Persistence.Repository.Repositories; +//namespace DD.Persistence.Repository.Repositories; -/// -/// Репозиторий для хранения разных наборов данных временных рядов. -/// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest. -/// idDiscriminator формируют клиенты и только им известно что они обозначают. -/// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено. -/// -public class TimestampedSetRepository : ITimestampedSetRepository -{ - private readonly DbContext db; +///// +///// Репозиторий для хранения разных наборов данных временных рядов. +///// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest. +///// idDiscriminator формируют клиенты и только им известно что они обозначают. +///// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено. +///// +//public class TimestampedSetRepository : ITimestampedSetRepository +//{ +// private readonly DbContext db; - public TimestampedSetRepository(DbContext db) - { - this.db = db; - } +// public TimestampedSetRepository(DbContext db) +// { +// this.db = db; +// } - public Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token) - { - var entities = sets.Select(set => new TimestampedSet(idDiscriminator, set.Timestamp.ToUniversalTime(), set.Set)); - var dbSet = db.Set(); - dbSet.AddRange(entities); - return db.SaveChangesAsync(token); - } +// public Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token) +// { +// var entities = sets.Select(set => new TimestampedSet(idDiscriminator, set.Timestamp.ToUniversalTime(), set.Set)); +// var dbSet = db.Set(); +// dbSet.AddRange(entities); +// return db.SaveChangesAsync(token); +// } - public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) - { - var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); +// public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) +// { +// var dbSet = db.Set(); +// var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); - if (geTimestamp.HasValue) - query = ApplyGeTimestamp(query, geTimestamp.Value); +// if (geTimestamp.HasValue) +// query = ApplyGeTimestamp(query, geTimestamp.Value); - query = query - .OrderBy(item => item.Timestamp) - .Skip(skip) - .Take(take); +// query = query +// .OrderBy(item => item.Timestamp) +// .Skip(skip) +// .Take(take); - var data = await Materialize(query, token); +// var data = await Materialize(query, token); - if (columnNames is not null && columnNames.Any()) - data = ReduceSetColumnsByNames(data, columnNames); +// if (columnNames is not null && columnNames.Any()) +// data = ReduceSetColumnsByNames(data, columnNames); - return data; - } +// return data; +// } - public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) - { - var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); +// public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) +// { +// var dbSet = db.Set(); +// var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); - query = query.OrderByDescending(entity => entity.Timestamp) - .Take(take) - .OrderBy(entity => entity.Timestamp); +// query = query.OrderByDescending(entity => entity.Timestamp) +// .Take(take) +// .OrderBy(entity => entity.Timestamp); - var data = await Materialize(query, token); +// var data = await Materialize(query, token); - if (columnNames is not null && columnNames.Any()) - data = ReduceSetColumnsByNames(data, columnNames); +// if (columnNames is not null && columnNames.Any()) +// data = ReduceSetColumnsByNames(data, columnNames); - return data; - } +// return data; +// } - public Task Count(Guid idDiscriminator, CancellationToken token) - { - var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); - return query.CountAsync(token); - } +// public Task Count(Guid idDiscriminator, CancellationToken token) +// { +// var dbSet = db.Set(); +// var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); +// return query.CountAsync(token); +// } - public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) - { - var query = db.Set() - .GroupBy(entity => entity.IdDiscriminator) - .Select(group => new - { - Min = group.Min(entity => entity.Timestamp), - Max = group.Max(entity => entity.Timestamp), - }); +// public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) +// { +// var query = db.Set() +// .GroupBy(entity => entity.IdDiscriminator) +// .Select(group => new +// { +// Min = group.Min(entity => entity.Timestamp), +// Max = group.Max(entity => entity.Timestamp), +// }); - var item = await query.FirstOrDefaultAsync(token); - if (item is null) - return null; +// var item = await query.FirstOrDefaultAsync(token); +// if (item is null) +// return null; - return new DatesRangeDto - { - From = item.Min, - To = item.Max, - }; - } +// return new DatesRangeDto +// { +// From = item.Min, +// To = item.Max, +// }; +// } - private static async Task> Materialize(IQueryable query, CancellationToken token) - { - var dtoQuery = query.Select(entity => new TimestampedSetDto(entity.Timestamp, entity.Set)); - var dtos = await dtoQuery.ToArrayAsync(token); - return dtos; - } +// private static async Task> Materialize(IQueryable query, CancellationToken token) +// { +// var dtoQuery = query.Select(entity => new TimestampedSetDto(entity.Timestamp, entity.Set)); +// var dtos = await dtoQuery.ToArrayAsync(token); +// return dtos; +// } - private static IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset geTimestamp) - { - var geTimestampUtc = geTimestamp.ToUniversalTime(); - return query.Where(entity => entity.Timestamp >= geTimestampUtc); - } +// private static IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset geTimestamp) +// { +// var geTimestampUtc = geTimestamp.ToUniversalTime(); +// return query.Where(entity => entity.Timestamp >= geTimestampUtc); +// } - private static IEnumerable ReduceSetColumnsByNames(IEnumerable query, IEnumerable columnNames) - { - var newQuery = query - .Select(entity => new TimestampedSetDto( - entity.Timestamp, - entity.Set - .Where(prop => columnNames.Contains(prop.Key)) - .ToDictionary(prop => prop.Key, prop => prop.Value) - )); - return newQuery; - } -} +// private static IEnumerable ReduceSetColumnsByNames(IEnumerable query, IEnumerable columnNames) +// { +// var newQuery = query +// .Select(entity => new TimestampedSetDto( +// entity.Timestamp, +// entity.Set +// .Where(prop => columnNames.Contains(prop.Key)) +// .ToDictionary(prop => prop.Key, prop => prop.Value) +// )); +// return newQuery; +// } +//} diff --git a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs index d055594..9ab1877 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs @@ -1,9 +1,11 @@ using DD.Persistence.Database.Entity; +using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.ModelsAbstractions; using DD.Persistence.Repositories; using Mapster; using Microsoft.EntityFrameworkCore; +using System.Text.Json; namespace DD.Persistence.Repository.Repositories; public class TimestampedValuesRepository : ITimestampedValuesRepository @@ -18,20 +20,28 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository GetQueryReadOnly() => this.db.Set(); - public virtual async Task GetDatesRange(CancellationToken token) + public virtual async Task GetDatesRange(Guid discriminatorId, CancellationToken token) { - var query = GetQueryReadOnly(); - var minDate = await query.MinAsync(o => o.Timestamp, token); - var maxDate = await query.MaxAsync(o => o.Timestamp, token); + var query = GetQueryReadOnly() + .GroupBy(entity => entity.DiscriminatorId) + .Select(group => new + { + Min = group.Min(entity => entity.Timestamp), + Max = group.Max(entity => entity.Timestamp), + }); + + var item = await query.FirstOrDefaultAsync(token); + if (item is null) + return null; return new DatesRangeDto { - From = minDate, - To = maxDate + From = item.Min, + To = item.Max, }; } - public virtual async Task> GetGtDate(DateTimeOffset date, CancellationToken token) + public virtual async Task> GetGtDate(Guid discriminatorId, DateTimeOffset date, CancellationToken token) { var query = GetQueryReadOnly().Where(e => e.Timestamp > date); var entities = await query.ToArrayAsync(token); @@ -41,9 +51,13 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository AddRange(IEnumerable dtos, CancellationToken token) + public virtual async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) { - var entities = dtos.Select(d => d.Adapt()); + var entities = dtos + .Select(d => d.Adapt()) + .ToList(); + + entities.ForEach(d => d.DiscriminatorId = discriminatorId); await db.Set().AddRangeAsync(entities, token); var result = await db.SaveChangesAsync(token); @@ -78,12 +92,13 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository> GetResampledData( + Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) { - var dtos = await GetGtDate(dateBegin, token); + var dtos = await GetGtDate(discriminatorId, dateBegin, token); var dateEnd = dateBegin.AddSeconds(intervalSec); dtos = dtos @@ -96,4 +111,77 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + { + var dbSet = db.Set(); + var query = dbSet.Where(entity => entity.DiscriminatorId == idDiscriminator); + + if (geTimestamp.HasValue) + query = ApplyGeTimestamp(query, geTimestamp.Value); + + query = query + .OrderBy(item => item.Timestamp) + .Skip(skip) + .Take(take); + + var data = await Materialize(query, token); + + if (columnNames is not null && columnNames.Any()) + data = ReduceSetColumnsByNames(data, columnNames); + + return data; + } + + public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) + { + var dbSet = db.Set(); + var query = dbSet.Where(entity => entity.DiscriminatorId == idDiscriminator); + + query = query.OrderByDescending(entity => entity.Timestamp) + .Take(take) + .OrderBy(entity => entity.Timestamp); + + var data = await Materialize(query, token); + + if (columnNames is not null && columnNames.Any()) + data = ReduceSetColumnsByNames(data, columnNames); + + return data; + } + + public Task Count(Guid idDiscriminator, CancellationToken token) + { + var dbSet = db.Set(); + var query = dbSet.Where(entity => entity.DiscriminatorId == idDiscriminator); + return query.CountAsync(token); + } + + private static async Task> Materialize(IQueryable query, CancellationToken token) + { + var dtoQuery = query.Select(entity => new TimestampedValuesDto() { Timestamp = entity.Timestamp, Values = entity.Values }); + var dtos = await dtoQuery.ToArrayAsync(token); + return dtos; + } + + private static IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset geTimestamp) + { + var geTimestampUtc = geTimestamp.ToUniversalTime(); + return query.Where(entity => entity.Timestamp >= geTimestampUtc); + } + + private static IEnumerable ReduceSetColumnsByNames(IEnumerable query, IEnumerable columnNames) + { + var newQuery = query + .Select(entity => new TimestampedValuesDto() + { + Timestamp = entity.Timestamp, + Values = entity.Values? + .Where(prop => columnNames.Contains( + JsonSerializer.Deserialize>(prop.ToString()!)? + .FirstOrDefault().Key + )).ToArray() + }); + return newQuery; + } } diff --git a/DD.Persistence/Models/TimestampedSetDto.cs b/DD.Persistence/Models/TimestampedValuesDto.cs similarity index 62% rename from DD.Persistence/Models/TimestampedSetDto.cs rename to DD.Persistence/Models/TimestampedValuesDto.cs index 20c5cbb..938970d 100644 --- a/DD.Persistence/Models/TimestampedSetDto.cs +++ b/DD.Persistence/Models/TimestampedValuesDto.cs @@ -1,9 +1,11 @@ -namespace DD.Persistence.Models; +using DD.Persistence.ModelsAbstractions; + +namespace DD.Persistence.Models; /// /// Набор данных с отметкой времени /// -public class TimestampedSetDto +public class TimestampedValuesDto : ITimestampAbstractDto { /// /// Временная отметка @@ -13,5 +15,5 @@ public class TimestampedSetDto /// /// Набор данных /// - public required object[] Set { get; set; } + public object[]? Values { get; set; } } diff --git a/DD.Persistence/Repositories/ITimestampedSetRepository.cs b/DD.Persistence/Repositories/ITimestampedSetRepository.cs deleted file mode 100644 index c448744..0000000 --- a/DD.Persistence/Repositories/ITimestampedSetRepository.cs +++ /dev/null @@ -1,60 +0,0 @@ -using DD.Persistence.Models; -using DD.Persistence.Models.Common; - -namespace DD.Persistence.Repositories; - -/// -/// Репозиторий для хранения разных наборов данных рядов. -/// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest. -/// idDiscriminator формируют клиенты и только им известно что они обозначают. -/// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено. -/// -public interface ITimestampedSetRepository -{ - /// - /// Количество записей по указанному набору в БД. Для пагинации. - /// - /// Дискриминатор (идентификатор) набора - /// - /// - Task Count(Guid idDiscriminator, CancellationToken token); - - /// - /// Получение данных с фильтрацией. Значение фильтра null - отключен - /// - /// Дискриминатор (идентификатор) набора - /// Фильтр позднее даты - /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора - /// - /// - /// - /// - Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); - - /// - /// Диапазон дат за которые есть данные - /// - /// Дискриминатор (идентификатор) набора - /// - /// - Task GetDatesRange(Guid idDiscriminator, CancellationToken token); - - /// - /// Получить последние данные - /// - /// Дискриминатор (идентификатор) набора - /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора - /// - /// - /// - Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token); - - /// - /// Добавление новых данных - /// - /// Дискриминатор (идентификатор) набора - /// - /// - /// - Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); -} \ No newline at end of file diff --git a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs index 447d197..5a508c4 100644 --- a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs +++ b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs @@ -1,5 +1,6 @@ using DD.Persistence.Models; using DD.Persistence.ModelsAbstractions; +using DD.Persistence.RepositoriesAbstractions; namespace DD.Persistence.Repositories; @@ -7,14 +8,45 @@ namespace DD.Persistence.Repositories; /// Интерфейс по работе с временными данными /// /// -public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBaseRepository +public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBaseRepository where TDto : class, ITimestampAbstractDto, new() { /// /// Добавление записей /// + /// /// /// /// - Task AddRange(IEnumerable dtos, CancellationToken token); + Task AddRange(Guid idDiscriminator, IEnumerable dtos, CancellationToken token); + + /// + /// Количество записей по указанному набору в БД. Для пагинации. + /// + /// Дискриминатор (идентификатор) набора + /// + /// + Task Count(Guid idDiscriminator, CancellationToken token); + + /// + /// Получить последние данные + /// + /// Дискриминатор (идентификатор) набора + /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора + /// + /// + /// + Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token); + + /// + /// Получение данных с фильтрацией. Значение фильтра null - отключен + /// + /// Дискриминатор (идентификатор) набора + /// Фильтр позднее даты + /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора + /// + /// + /// + /// + Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); } diff --git a/DD.Persistence/Repositories/ISyncRepository.cs b/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs similarity index 60% rename from DD.Persistence/Repositories/ISyncRepository.cs rename to DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs index 9a43b01..4db2645 100644 --- a/DD.Persistence/Repositories/ISyncRepository.cs +++ b/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs @@ -1,6 +1,6 @@ using DD.Persistence.Models.Common; -namespace DD.Persistence.Repositories; +namespace DD.Persistence.RepositoriesAbstractions; /// /// Интерфейс по работе с данными @@ -11,15 +11,18 @@ public interface ISyncRepository /// /// Получить данные, начиная с определенной даты /// + /// /// дата начала - /// /// - Task> GetGtDate(DateTimeOffset dateBegin, CancellationToken token); + /// + /// + Task> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token); /// /// Получить диапазон дат, для которых есть данные в репозитории /// + /// /// /// - Task GetDatesRange(CancellationToken token); + Task GetDatesRange(Guid discriminatorId, CancellationToken token); } diff --git a/DD.Persistence/Repositories/ITimeSeriesBaseRepository.cs b/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs similarity index 65% rename from DD.Persistence/Repositories/ITimeSeriesBaseRepository.cs rename to DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs index 5f472e0..9fd43b4 100644 --- a/DD.Persistence/Repositories/ITimeSeriesBaseRepository.cs +++ b/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs @@ -1,19 +1,23 @@ using DD.Persistence.Models; -namespace DD.Persistence.Repositories; +namespace DD.Persistence.RepositoriesAbstractions; /// /// Интерфейс по работе с прореженными данными /// -public interface ITimeSeriesBaseRepository +public interface ITimeSeriesBaseRepository { /// /// Получить список объектов с прореживанием /// + /// /// дата начала + /// /// + /// /// - Task> GetResampledData( + Task> GetResampledData( + Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, -- 2.45.2 From 9736b41f1a3ea9fe5cf4e687aee75c7b64f8f575 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 15 Jan 2025 17:37:48 +0500 Subject: [PATCH 04/16] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=D0=B0,=20?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ller.cs => TimestampedValuesController.cs} | 4 +- DD.Persistence.App/appsettings.Tests.json | 2 +- DD.Persistence.App/appsettings.json | 2 +- .../Clients/Interfaces/ITimeSeriesClient.cs | 45 ------- .../Interfaces/ITimestampedSetClient.cs | 60 --------- .../Interfaces/ITimestampedValuesClient.cs | 70 ++++++++++ .../Refit/IRefitTimeSeriesClient.cs | 22 ---- .../Refit/IRefitTimestampedValuesClient.cs | 8 +- .../Clients/TimeSeriesClient.cs | 56 -------- .../Clients/TimestampedSetClient.cs | 64 --------- .../Clients/TimestampedValuesClient.cs | 72 +++++++++++ DD.Persistence.Client/DependencyInjection.cs | 2 +- .../Controllers/DataSaubControllerTest.cs | 85 ------------ .../TimeSeriesBaseControllerTest.cs | 122 ------------------ .../TimestampedSetControllerTest.cs | 41 ++++-- .../TimestampedValuesRepository.cs | 6 +- 16 files changed, 184 insertions(+), 477 deletions(-) rename DD.Persistence.API/Controllers/{TimestampedSetController.cs => TimestampedValuesController.cs} (97%) delete mode 100644 DD.Persistence.Client/Clients/Interfaces/ITimeSeriesClient.cs delete mode 100644 DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs create mode 100644 DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs delete mode 100644 DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimeSeriesClient.cs delete mode 100644 DD.Persistence.Client/Clients/TimeSeriesClient.cs delete mode 100644 DD.Persistence.Client/Clients/TimestampedSetClient.cs create mode 100644 DD.Persistence.Client/Clients/TimestampedValuesClient.cs delete mode 100644 DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs delete mode 100644 DD.Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs diff --git a/DD.Persistence.API/Controllers/TimestampedSetController.cs b/DD.Persistence.API/Controllers/TimestampedValuesController.cs similarity index 97% rename from DD.Persistence.API/Controllers/TimestampedSetController.cs rename to DD.Persistence.API/Controllers/TimestampedValuesController.cs index 878ee3d..a059243 100644 --- a/DD.Persistence.API/Controllers/TimestampedSetController.cs +++ b/DD.Persistence.API/Controllers/TimestampedValuesController.cs @@ -13,11 +13,11 @@ namespace DD.Persistence.API.Controllers; [ApiController] //[Authorize] [Route("api/[controller]/{discriminatorId}")] -public class TimestampedSetController : ControllerBase +public class TimestampedValuesController : ControllerBase { private readonly ITimestampedValuesRepository repository; - public TimestampedSetController(ITimestampedValuesRepository repository) + public TimestampedValuesController(ITimestampedValuesRepository repository) { this.repository = repository; } diff --git a/DD.Persistence.App/appsettings.Tests.json b/DD.Persistence.App/appsettings.Tests.json index 9934757..e8d3cc4 100644 --- a/DD.Persistence.App/appsettings.Tests.json +++ b/DD.Persistence.App/appsettings.Tests.json @@ -4,7 +4,7 @@ "Port": 5432, "Database": "persistence", "Username": "postgres", - "Password": "postgres" + "Password": "q" }, "NeedUseKeyCloak": false, "AuthUser": { diff --git a/DD.Persistence.App/appsettings.json b/DD.Persistence.App/appsettings.json index 7ad8c67..0af1529 100644 --- a/DD.Persistence.App/appsettings.json +++ b/DD.Persistence.App/appsettings.json @@ -6,7 +6,7 @@ } }, "ConnectionStrings": { - "DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True" + "DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=q;Persist Security Info=True" }, "AllowedHosts": "*", "NeedUseKeyCloak": false, diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimeSeriesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimeSeriesClient.cs deleted file mode 100644 index c41564c..0000000 --- a/DD.Persistence.Client/Clients/Interfaces/ITimeSeriesClient.cs +++ /dev/null @@ -1,45 +0,0 @@ -using DD.Persistence.Models.Common; -using DD.Persistence.ModelsAbstractions; - -namespace DD.Persistence.Client.Clients.Interfaces; - -/// -/// Клиент для работы с временными данными -/// -/// -public interface ITimeSeriesClient : IDisposable where TDto : class, ITimestampAbstractDto -{ - /// - /// Добавление записей - /// - /// - /// - /// - Task AddRange(IEnumerable dtos, CancellationToken token); - - /// - /// Получить список объектов, удовлетворяющий диапазону дат - /// - /// - /// - /// - /// - Task> Get(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token); - - /// - /// Получить диапазон дат, для которых есть данные в репозитории - /// - /// - /// - Task GetDatesRange(CancellationToken token); - - /// - /// Получить список объектов с прореживанием, удовлетворяющий диапазону дат - /// - /// - /// - /// - /// - /// - Task> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default); -} \ No newline at end of file diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs deleted file mode 100644 index 701beb9..0000000 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs +++ /dev/null @@ -1,60 +0,0 @@ -using DD.Persistence.Models; -using DD.Persistence.Models.Common; - -namespace DD.Persistence.Client.Clients.Interfaces; - -/// -/// Клиент для работы с репозиторием для хранения разных наборов данных рядов. -/// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest. -/// idDiscriminator формируют клиенты и только им известно что они обозначают. -/// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено. -/// -public interface ITimestampedSetClient : IDisposable -{ - /// - /// Записать новые данные - /// - /// - /// - /// - /// - Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); - - /// - /// Количество записей по указанному набору в БД. Для пагинации - /// - /// - /// - /// - Task Count(Guid idDiscriminator, CancellationToken token); - - /// - /// Получение данных с фильтрацией. Значение фильтра null - отключен - /// - /// - /// - /// - /// - /// - /// - /// - Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); - - /// - /// Диапазон дат за которые есть данные - /// - /// - /// - /// - Task GetDatesRange(Guid idDiscriminator, CancellationToken token); - - /// - /// - /// - /// - /// - /// - /// - /// - Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token); -} \ No newline at end of file diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs new file mode 100644 index 0000000..ea1f576 --- /dev/null +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs @@ -0,0 +1,70 @@ +using DD.Persistence.Models; +using DD.Persistence.Models.Common; + +namespace DD.Persistence.Client.Clients.Interfaces; + +/// +/// Клиент для работы с репозиторием для хранения разных наборов данных рядов. +/// discriminatorId - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest. +/// discriminatorId формируют клиенты и только им известно что они обозначают. +/// +public interface ITimestampedValuesClient : IDisposable +{ + /// + /// Записать новые данные + /// + /// + /// + /// + /// + Task AddRange(Guid discriminatorId, IEnumerable sets, CancellationToken token); + + /// + /// Количество записей по указанному набору в БД. Для пагинации + /// + /// + /// + /// + Task Count(Guid discriminatorId, CancellationToken token); + + /// + /// Получение данных с фильтрацией. Значение фильтра null - отключен + /// + /// + /// + /// + /// + /// + /// + /// + Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); + + /// + /// Диапазон дат за которые есть данные + /// + /// + /// + /// + Task GetDatesRange(Guid discriminatorId, CancellationToken token); + + /// + /// Получить последние данные + /// + /// + /// + /// + /// + /// + Task> GetLast(Guid discriminatorId, IEnumerable? columnNames, int take, CancellationToken token); + + /// + /// Получить список объектов с прореживанием, удовлетворяющий диапазону дат + /// + /// + /// + /// + /// + /// + /// + Task> GetResampledData(Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); +} \ No newline at end of file diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimeSeriesClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimeSeriesClient.cs deleted file mode 100644 index 0dc4994..0000000 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimeSeriesClient.cs +++ /dev/null @@ -1,22 +0,0 @@ -using DD.Persistence.Models.Common; -using DD.Persistence.ModelsAbstractions; -using Refit; - -namespace DD.Persistence.Client.Clients.Interfaces.Refit; -public interface IRefitTimeSeriesClient : IRefitClient, IDisposable - where TDto : class, ITimestampAbstractDto -{ - private const string BaseRoute = "/api/dataSaub"; - - [Post($"{BaseRoute}")] - Task> AddRange(IEnumerable dtos, CancellationToken token); - - [Get($"{BaseRoute}")] - Task>> Get(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token); - - [Get($"{BaseRoute}/resampled")] - Task>> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); - - [Get($"{BaseRoute}/datesRange")] - Task> GetDatesRange(CancellationToken token); -} diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs index bf7a386..2515a57 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs @@ -6,13 +6,13 @@ namespace DD.Persistence.Client.Clients.Interfaces.Refit; public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable { - private const string baseUrl = "/api/TimestampedSet/{idDiscriminator}"; + private const string baseUrl = "/api/TimestampedValues/{discriminatorId}"; [Post(baseUrl)] - Task> AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); + Task> AddRange(Guid discriminatorId, IEnumerable sets, CancellationToken token); [Get(baseUrl)] - Task>> Get(Guid idDiscriminator, [Query] DateTimeOffset? geTimestamp, [Query] IEnumerable? columnNames, int skip, int take, CancellationToken token); + Task>> Get(Guid discriminatorId, [Query] DateTimeOffset? geTimestamp, [Query] IEnumerable? columnNames, int skip, int take, CancellationToken token); [Get($"{baseUrl}/last")] Task>> GetLast(Guid discriminatorId, [Query] IEnumerable? columnNames, int take, CancellationToken token); @@ -24,5 +24,5 @@ public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable Task> GetDatesRange(Guid discriminatorId, CancellationToken token); [Get($"{baseUrl}/resampled")] - Task>> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); + Task>> GetResampledData(Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); } diff --git a/DD.Persistence.Client/Clients/TimeSeriesClient.cs b/DD.Persistence.Client/Clients/TimeSeriesClient.cs deleted file mode 100644 index 10ff2e4..0000000 --- a/DD.Persistence.Client/Clients/TimeSeriesClient.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Microsoft.Extensions.Logging; -using DD.Persistence.Client.Clients.Base; -using DD.Persistence.Client.Clients.Interfaces; -using DD.Persistence.Client.Clients.Interfaces.Refit; -using DD.Persistence.Models.Common; -using DD.Persistence.ModelsAbstractions; - -namespace DD.Persistence.Client.Clients; -public class TimeSeriesClient : BaseClient, ITimeSeriesClient where TDto : class, ITimestampAbstractDto -{ - private readonly IRefitTimeSeriesClient timeSeriesClient; - - public TimeSeriesClient(IRefitClientFactory> refitTechMessagesClientFactory, ILogger> logger) : base(logger) - { - this.timeSeriesClient = refitTechMessagesClientFactory.Create(); - } - - public async Task AddRange(IEnumerable dtos, CancellationToken token) - { - var result = await ExecutePostResponse( - async () => await timeSeriesClient.AddRange(dtos, token), token); - - return result; - } - - public async Task> Get(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) - { - var result = await ExecuteGetResponse( - async () => await timeSeriesClient.Get(dateBegin, dateEnd, token), token); - - return result!; - } - - public async Task> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) - { - var result = await ExecuteGetResponse( - async () => await timeSeriesClient.GetResampledData(dateBegin, intervalSec, approxPointsCount, token), token); - - return result!; - } - - public async Task GetDatesRange(CancellationToken token) - { - var result = await ExecuteGetResponse( - async () => await timeSeriesClient.GetDatesRange(token), token); - - return result; - } - - public void Dispose() - { - timeSeriesClient.Dispose(); - - GC.SuppressFinalize(this); - } -} diff --git a/DD.Persistence.Client/Clients/TimestampedSetClient.cs b/DD.Persistence.Client/Clients/TimestampedSetClient.cs deleted file mode 100644 index 14fdee9..0000000 --- a/DD.Persistence.Client/Clients/TimestampedSetClient.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.Extensions.Logging; -using DD.Persistence.Client.Clients.Base; -using DD.Persistence.Client.Clients.Interfaces; -using DD.Persistence.Client.Clients.Interfaces.Refit; -using DD.Persistence.Models; -using DD.Persistence.Models.Common; - -namespace DD.Persistence.Client.Clients; -public class TimestampedSetClient : BaseClient, ITimestampedSetClient -{ - private readonly IRefitTimestampedValuesClient refitTimestampedSetClient; - - public TimestampedSetClient(IRefitClientFactory refitTimestampedSetClientFactory, ILogger logger) : base(logger) - { - this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create(); - } - - public async Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token) - { - var result = await ExecutePostResponse( - async () => await refitTimestampedSetClient.AddRange(idDiscriminator, sets, token), token); - - return result; - } - - public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) - { - var result = await ExecuteGetResponse( - async () => await refitTimestampedSetClient.Get(idDiscriminator, geTimestamp, columnNames, skip, take, token), token); - - return result!; - } - - public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) - { - var result = await ExecuteGetResponse( - async () => await refitTimestampedSetClient.GetLast(idDiscriminator, columnNames, take, token), token); - - return result!; - } - - public async Task Count(Guid idDiscriminator, CancellationToken token) - { - var result = await ExecuteGetResponse( - async () => await refitTimestampedSetClient.Count(idDiscriminator, token), token); - - return result; - } - - public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) - { - var result = await ExecuteGetResponse( - async () => await refitTimestampedSetClient.GetDatesRange(idDiscriminator, token), token); - - return result; - } - - public void Dispose() - { - refitTimestampedSetClient.Dispose(); - - GC.SuppressFinalize(this); - } -} diff --git a/DD.Persistence.Client/Clients/TimestampedValuesClient.cs b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs new file mode 100644 index 0000000..d32e9b9 --- /dev/null +++ b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs @@ -0,0 +1,72 @@ +using Microsoft.Extensions.Logging; +using DD.Persistence.Client.Clients.Base; +using DD.Persistence.Client.Clients.Interfaces; +using DD.Persistence.Client.Clients.Interfaces.Refit; +using DD.Persistence.Models; +using DD.Persistence.Models.Common; + +namespace DD.Persistence.Client.Clients; +public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient +{ + private readonly IRefitTimestampedValuesClient refitTimestampedSetClient; + + public TimestampedValuesClient(IRefitClientFactory refitTimestampedSetClientFactory, ILogger logger) : base(logger) + { + this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create(); + } + + public async Task AddRange(Guid discriminatorId, IEnumerable sets, CancellationToken token) + { + var result = await ExecutePostResponse( + async () => await refitTimestampedSetClient.AddRange(discriminatorId, sets, token), token); + + return result; + } + + public async Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + { + var result = await ExecuteGetResponse( + async () => await refitTimestampedSetClient.Get(discriminatorId, geTimestamp, columnNames, skip, take, token), token); + + return result!; + } + + public async Task> GetLast(Guid discriminatorId, IEnumerable? columnNames, int take, CancellationToken token) + { + var result = await ExecuteGetResponse( + async () => await refitTimestampedSetClient.GetLast(discriminatorId, columnNames, take, token), token); + + return result!; + } + + public async Task Count(Guid discriminatorId, CancellationToken token) + { + var result = await ExecuteGetResponse( + async () => await refitTimestampedSetClient.Count(discriminatorId, token), token); + + return result; + } + + public async Task GetDatesRange(Guid discriminatorId, CancellationToken token) + { + var result = await ExecuteGetResponse( + async () => await refitTimestampedSetClient.GetDatesRange(discriminatorId, token), token); + + return result; + } + + public async Task> GetResampledData(Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default) + { + var result = await ExecuteGetResponse( + async () => await refitTimestampedSetClient.GetResampledData(discriminatorId, dateBegin, intervalSec, approxPointsCount, token), token); + + return result; + } + + public void Dispose() + { + refitTimestampedSetClient.Dispose(); + + GC.SuppressFinalize(this); + } +} diff --git a/DD.Persistence.Client/DependencyInjection.cs b/DD.Persistence.Client/DependencyInjection.cs index 43a8733..422cf8f 100644 --- a/DD.Persistence.Client/DependencyInjection.cs +++ b/DD.Persistence.Client/DependencyInjection.cs @@ -22,7 +22,7 @@ public static class DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); services.AddTransient(); return services; } diff --git a/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs deleted file mode 100644 index bb82675..0000000 --- a/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs +++ /dev/null @@ -1,85 +0,0 @@ -//using DD.Persistence.Database.Model; -//using DD.Persistence.Models; -//using Xunit; - -//namespace DD.Persistence.IntegrationTests.Controllers; -//public class DataSaubControllerTest : TimeSeriesBaseControllerTest -//{ -// private readonly DataSaubDto dto = new() -// { -// AxialLoad = 1, -// BitDepth = 2, -// BlockPosition = 3, -// BlockSpeed = 4, -// Date = DateTimeOffset.UtcNow, -// Flow = 5, -// HookWeight = 6, -// IdFeedRegulator = 8, -// Mode = 9, -// Mse = 10, -// MseState = 11, -// Pressure = 12, -// Pump0Flow = 13, -// Pump1Flow = 14, -// Pump2Flow = 15, -// RotorSpeed = 16, -// RotorTorque = 17, -// User = string.Empty, -// WellDepth = 18, -// }; - -// private readonly DataSaub entity = new() -// { -// AxialLoad = 1, -// BitDepth = 2, -// BlockPosition = 3, -// BlockSpeed = 4, -// Timestamp = DateTimeOffset.UtcNow, -// Flow = 5, -// HookWeight = 6, -// IdFeedRegulator = 8, -// Mode = 9, -// Mse = 10, -// MseState = 11, -// Pressure = 12, -// Pump0Flow = 13, -// Pump1Flow = 14, -// Pump2Flow = 15, -// RotorSpeed = 16, -// RotorTorque = 17, -// User = string.Empty, -// WellDepth = 18, -// }; - -// public DataSaubControllerTest(WebAppFactoryFixture factory) : base(factory) -// { -// } - -// [Fact] -// public async Task InsertRange_returns_success() -// { -// await InsertRangeSuccess(dto); -// } - -// [Fact] -// public async Task Get_returns_success() -// { -// var beginDate = DateTimeOffset.UtcNow.AddDays(-1); -// var endDate = DateTimeOffset.UtcNow; -// await GetSuccess(beginDate, endDate, entity); -// } - -// [Fact] -// public async Task GetDatesRange_returns_success() -// { -// await GetDatesRangeSuccess(entity); -// } - - - -// [Fact] -// public async Task GetResampledData_returns_success() -// { -// await GetResampledDataSuccess(entity); -// } -//} diff --git a/DD.Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs deleted file mode 100644 index 481bf57..0000000 --- a/DD.Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs +++ /dev/null @@ -1,122 +0,0 @@ -using DD.Persistence.Client; -using DD.Persistence.Client.Clients; -using DD.Persistence.Client.Clients.Interfaces; -using DD.Persistence.Client.Clients.Interfaces.Refit; -using DD.Persistence.Database.EntityAbstractions; -using DD.Persistence.ModelsAbstractions; -using Mapster; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Xunit; - -namespace DD.Persistence.IntegrationTests.Controllers; - -public abstract class TimeSeriesBaseControllerTest : BaseIntegrationTest - where TEntity : class, ITimestampedItem, new() - where TDto : class, ITimestampAbstractDto, new() -{ - private readonly ITimeSeriesClient timeSeriesClient; - - public TimeSeriesBaseControllerTest(WebAppFactoryFixture factory) : base(factory) - { - dbContext.CleanupDbSet(); - - var refitClientFactory = scope.ServiceProvider - .GetRequiredService>>(); - var logger = scope.ServiceProvider.GetRequiredService>>(); - - timeSeriesClient = scope.ServiceProvider - .GetRequiredService>(); - } - - public async Task InsertRangeSuccess(TDto dto) - { - //arrange - var expected = dto.Adapt(); - - //act - var response = await timeSeriesClient.AddRange(new TDto[] { expected }, new CancellationToken()); - - //assert - Assert.Equal(1, response); - } - - public async Task GetSuccess(DateTimeOffset beginDate, DateTimeOffset endDate, TEntity entity) - { - //arrange - var dbset = dbContext.Set(); - - dbset.Add(entity); - - dbContext.SaveChanges(); - - var response = await timeSeriesClient.Get(beginDate, endDate, new CancellationToken()); - - //assert - Assert.NotNull(response); - Assert.Single(response); - } - - public async Task GetDatesRangeSuccess(TEntity entity) - { - //arrange - var datesRangeExpected = 30; - - var entity2 = entity.Adapt(); - entity2.Timestamp = entity.Timestamp.AddDays(datesRangeExpected); - - var dbset = dbContext.Set(); - dbset.Add(entity); - dbset.Add(entity2); - - dbContext.SaveChanges(); - - var response = await timeSeriesClient.GetDatesRange(new CancellationToken()); - - //assert - Assert.NotNull(response); - - var datesRangeActual = (response.To - response.From).Days; - Assert.Equal(datesRangeExpected, datesRangeActual); - } - - public async Task GetResampledDataSuccess(TEntity entity) - { - //arrange - var approxPointsCount = 10; - var differenceBetweenStartAndEndDays = 50; - - var entities = new List(); - for (var i = 1; i <= differenceBetweenStartAndEndDays; i++) - { - var entity2 = entity.Adapt(); - entity2.Timestamp = entity.Timestamp.AddDays(i - 1); - - entities.Add(entity2); - } - - var dbset = dbContext.Set(); - dbset.AddRange(entities); - - dbContext.SaveChanges(); - - var response = await timeSeriesClient.GetResampledData(entity.Timestamp.AddMinutes(-1), differenceBetweenStartAndEndDays * 24 * 60 * 60 + 60, approxPointsCount, new CancellationToken()); - - //assert - Assert.NotNull(response); - - var ratio = entities.Count / approxPointsCount; - if (ratio > 1) - { - var expectedResampledCount = entities - .Where((_, index) => index % ratio == 0) - .Count(); - - Assert.Equal(expectedResampledCount, response.Count()); - } - else - { - Assert.Equal(entities.Count(), response.Count()); - } - } -} diff --git a/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs index c929230..a8135c6 100644 --- a/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs @@ -6,20 +6,25 @@ using Xunit; using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Client.Clients; using Microsoft.Extensions.Logging; +using System.Text.Json; +using System.Text; +using Microsoft.Extensions.Primitives; +using System.Dynamic; +using Newtonsoft.Json.Linq; namespace DD.Persistence.IntegrationTests.Controllers; public class TimestampedSetControllerTest : BaseIntegrationTest { - private readonly ITimestampedSetClient client; + private readonly ITimestampedValuesClient client; public TimestampedSetControllerTest(WebAppFactoryFixture factory) : base(factory) { var refitClientFactory = scope.ServiceProvider .GetRequiredService>(); - var logger = scope.ServiceProvider.GetRequiredService>(); + var logger = scope.ServiceProvider.GetRequiredService>(); client = scope.ServiceProvider - .GetRequiredService(); + .GetRequiredService(); } [Fact] @@ -71,8 +76,8 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Assert.Equal(count, response.Count()); foreach (var item in response) { - Assert.Single(item.Values); - var kv = item.Values.First(); + Assert.Single(item.Values!); + var kv = item.Values!.First(); Assert.Equal("A", ((KeyValuePair) kv).Key); } } @@ -196,18 +201,28 @@ public class TimestampedSetControllerTest : BaseIntegrationTest private static IEnumerable Generate(int n, DateTimeOffset from) { + var result = new List(); for (int i = 0; i < n; i++) + { + var t = new object[] { + new { A = i }, + new { B = i * 1.1 }, + new { C = $"Any{i}" }, + new { D = DateTimeOffset.Now } + }; + string jsonString = JsonSerializer.Serialize(t); + var values = JsonSerializer.Deserialize(jsonString); + //// + //dynamic obj = new ExpandoObject(); + + //// "FieldName" 123 + //obj.FieldName = 123; + yield return new TimestampedValuesDto() { Timestamp = from.AddSeconds(i), - Values = new Dictionary { - {"A", i }, - {"B", i * 1.1 }, - {"C", $"Any{i}" }, - {"D", DateTimeOffset.Now} - } - .Select(e => (object) e) - .ToArray() + Values = values }; + } } } diff --git a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs index 9ab1877..cc892ad 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs @@ -57,7 +57,11 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository d.Adapt()) .ToList(); - entities.ForEach(d => d.DiscriminatorId = discriminatorId); + entities.ForEach(d => + { + d.DiscriminatorId = discriminatorId; + d.Timestamp = d.Timestamp.ToUniversalTime(); + }); await db.Set().AddRangeAsync(entities, token); var result = await db.SaveChangesAsync(token); -- 2.45.2 From 556fdcbca07c8b47958c5b01e9bd3ea84ba68d1d Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Thu, 16 Jan 2025 17:19:27 +0500 Subject: [PATCH 05/16] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=B4=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=BB=D1=8C=D0=BD=D1=83=D1=8E=20=D1=81=D1=83?= =?UTF-8?q?=D1=89=D0=BD=D0=BE=D1=81=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/DataSourceSystemController.cs | 4 +- .../TimestampedValuesController.cs | 4 +- DD.Persistence.App/appsettings.Tests.json | 2 +- ...ner.cs => 20250116093615_Init.Designer.cs} | 32 +++- ...4100429_Init.cs => 20250116093615_Init.cs} | 31 +++- ...PersistencePostgresContextModelSnapshot.cs | 30 +++- .../Entity/DataSourceSystem.cs | 2 +- .../Entity/TimestampedValues.cs | 3 + .../Entity/ValuesIdentity.cs | 14 ++ .../PersistenceDbContext.cs | 8 +- .../TimestampedSetControllerTest.cs | 4 - .../DependencyInjection.cs | 9 +- .../DataSourceSystemRepository.cs | 33 ---- .../Repositories/RelatedDataRepository.cs | 33 ++++ .../Repositories/TechMessagesRepository.cs | 13 +- .../Repositories/TimestampedSetRepository.cs | 122 -------------- .../TimestampedValuesRepository.cs | 150 +++++++++++++----- ...tory.cs => RelatedDataCachedRepository.cs} | 10 +- DD.Persistence/Models/DataSourceSystemDto.cs | 2 +- DD.Persistence/Models/TimestampedValuesDto.cs | 2 +- DD.Persistence/Models/ValuesIdentityDto.cs | 17 ++ .../IDataSourceSystemRepository.cs | 22 --- .../Repositories/IRelatedDataRepository.cs | 23 +++ .../ITimestampedValuesRepository.cs | 6 +- .../ISyncRepository.cs | 8 +- .../ITimeSeriesBaseRepository.cs | 4 +- 26 files changed, 322 insertions(+), 266 deletions(-) rename DD.Persistence.Database.Postgres/Migrations/{20250114100429_Init.Designer.cs => 20250116093615_Init.Designer.cs} (87%) rename DD.Persistence.Database.Postgres/Migrations/{20250114100429_Init.cs => 20250116093615_Init.cs} (86%) create mode 100644 DD.Persistence.Database/Entity/ValuesIdentity.cs delete mode 100644 DD.Persistence.Repository/Repositories/DataSourceSystemRepository.cs create mode 100644 DD.Persistence.Repository/Repositories/RelatedDataRepository.cs delete mode 100644 DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs rename DD.Persistence.Repository/RepositoriesCached/{DataSourceSystemCachedRepository.cs => RelatedDataCachedRepository.cs} (70%) create mode 100644 DD.Persistence/Models/ValuesIdentityDto.cs delete mode 100644 DD.Persistence/Repositories/IDataSourceSystemRepository.cs create mode 100644 DD.Persistence/Repositories/IRelatedDataRepository.cs diff --git a/DD.Persistence.API/Controllers/DataSourceSystemController.cs b/DD.Persistence.API/Controllers/DataSourceSystemController.cs index 896af1f..5e551a9 100644 --- a/DD.Persistence.API/Controllers/DataSourceSystemController.cs +++ b/DD.Persistence.API/Controllers/DataSourceSystemController.cs @@ -14,9 +14,9 @@ namespace DD.Persistence.API.Controllers; [Route("api/[controller]")] public class DataSourceSystemController : ControllerBase { - private readonly IDataSourceSystemRepository dataSourceSystemRepository; + private readonly IRelatedDataRepository dataSourceSystemRepository; - public DataSourceSystemController(IDataSourceSystemRepository dataSourceSystemRepository) + public DataSourceSystemController(IRelatedDataRepository dataSourceSystemRepository) { this.dataSourceSystemRepository = dataSourceSystemRepository; } diff --git a/DD.Persistence.API/Controllers/TimestampedValuesController.cs b/DD.Persistence.API/Controllers/TimestampedValuesController.cs index a059243..2b0b245 100644 --- a/DD.Persistence.API/Controllers/TimestampedValuesController.cs +++ b/DD.Persistence.API/Controllers/TimestampedValuesController.cs @@ -15,9 +15,9 @@ namespace DD.Persistence.API.Controllers; [Route("api/[controller]/{discriminatorId}")] public class TimestampedValuesController : ControllerBase { - private readonly ITimestampedValuesRepository repository; + private readonly ITimestampedValuesRepository repository; - public TimestampedValuesController(ITimestampedValuesRepository repository) + public TimestampedValuesController(ITimestampedValuesRepository repository) { this.repository = repository; } diff --git a/DD.Persistence.App/appsettings.Tests.json b/DD.Persistence.App/appsettings.Tests.json index e8d3cc4..9934757 100644 --- a/DD.Persistence.App/appsettings.Tests.json +++ b/DD.Persistence.App/appsettings.Tests.json @@ -4,7 +4,7 @@ "Port": 5432, "Database": "persistence", "Username": "postgres", - "Password": "q" + "Password": "postgres" }, "NeedUseKeyCloak": false, "AuthUser": { diff --git a/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.Designer.cs b/DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.Designer.cs similarity index 87% rename from DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.Designer.cs rename to DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.Designer.cs index 8d0173d..0fa9552 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.Designer.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.Designer.cs @@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace DD.Persistence.Database.Postgres.Migrations { [DbContext(typeof(PersistencePostgresContext))] - [Migration("20250114100429_Init")] + [Migration("20250116093615_Init")] partial class Init { /// @@ -122,7 +122,24 @@ namespace DD.Persistence.Database.Postgres.Migrations b.HasKey("DiscriminatorId", "Timestamp"); - b.ToTable("TimestampedSets"); + b.ToTable("TimestampedValues"); + }); + + modelBuilder.Entity("DD.Persistence.Database.Entity.ValuesIdentity", b => + { + b.Property("DiscriminatorId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasComment("Дискриминатор системы"); + + b.Property("Identity") + .IsRequired() + .HasColumnType("jsonb") + .HasComment("Идентификаторы"); + + b.HasKey("DiscriminatorId"); + + b.ToTable("ValuesIdentities"); }); modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b => @@ -212,6 +229,17 @@ namespace DD.Persistence.Database.Postgres.Migrations b.Navigation("System"); }); + + modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b => + { + b.HasOne("DD.Persistence.Database.Entity.ValuesIdentity", "ValuesIdentity") + .WithMany() + .HasForeignKey("DiscriminatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ValuesIdentity"); + }); #pragma warning restore 612, 618 } } diff --git a/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.cs b/DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.cs similarity index 86% rename from DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.cs rename to DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.cs index 999a231..1898472 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.cs @@ -74,16 +74,15 @@ namespace DD.Persistence.Database.Postgres.Migrations }); migrationBuilder.CreateTable( - name: "TimestampedSets", + name: "ValuesIdentities", columns: table => new { - Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Временная отметка"), DiscriminatorId = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор системы"), - Values = table.Column(type: "jsonb", nullable: false, comment: "Данные") + Identity = table.Column(type: "jsonb", nullable: false, comment: "Идентификаторы") }, constraints: table => { - table.PrimaryKey("PK_TimestampedSets", x => new { x.DiscriminatorId, x.Timestamp }); + table.PrimaryKey("PK_ValuesIdentities", x => x.DiscriminatorId); }); migrationBuilder.CreateTable( @@ -108,6 +107,25 @@ namespace DD.Persistence.Database.Postgres.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "TimestampedValues", + columns: table => new + { + Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Временная отметка"), + DiscriminatorId = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор системы"), + Values = table.Column(type: "jsonb", nullable: false, comment: "Данные") + }, + constraints: table => + { + table.PrimaryKey("PK_TimestampedValues", x => new { x.DiscriminatorId, x.Timestamp }); + table.ForeignKey( + name: "FK_TimestampedValues_ValuesIdentities_DiscriminatorId", + column: x => x.DiscriminatorId, + principalTable: "ValuesIdentities", + principalColumn: "DiscriminatorId", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateIndex( name: "IX_TechMessage_SystemId", table: "TechMessage", @@ -130,10 +148,13 @@ namespace DD.Persistence.Database.Postgres.Migrations name: "TechMessage"); migrationBuilder.DropTable( - name: "TimestampedSets"); + name: "TimestampedValues"); migrationBuilder.DropTable( name: "DataSourceSystem"); + + migrationBuilder.DropTable( + name: "ValuesIdentities"); } } } diff --git a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs index 6e10f12..72e5d62 100644 --- a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs +++ b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs @@ -119,7 +119,24 @@ namespace DD.Persistence.Database.Postgres.Migrations b.HasKey("DiscriminatorId", "Timestamp"); - b.ToTable("TimestampedSets"); + b.ToTable("TimestampedValues"); + }); + + modelBuilder.Entity("DD.Persistence.Database.Entity.ValuesIdentity", b => + { + b.Property("DiscriminatorId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasComment("Дискриминатор системы"); + + b.Property("Identity") + .IsRequired() + .HasColumnType("jsonb") + .HasComment("Идентификаторы"); + + b.HasKey("DiscriminatorId"); + + b.ToTable("ValuesIdentities"); }); modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b => @@ -209,6 +226,17 @@ namespace DD.Persistence.Database.Postgres.Migrations b.Navigation("System"); }); + + modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b => + { + b.HasOne("DD.Persistence.Database.Entity.ValuesIdentity", "ValuesIdentity") + .WithMany() + .HasForeignKey("DiscriminatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ValuesIdentity"); + }); #pragma warning restore 612, 618 } } diff --git a/DD.Persistence.Database/Entity/DataSourceSystem.cs b/DD.Persistence.Database/Entity/DataSourceSystem.cs index d200731..cc282ac 100644 --- a/DD.Persistence.Database/Entity/DataSourceSystem.cs +++ b/DD.Persistence.Database/Entity/DataSourceSystem.cs @@ -9,7 +9,7 @@ public class DataSourceSystem public Guid SystemId { get; set; } [Required, Column(TypeName = "varchar(256)"), Comment("Наименование системы - источника данных")] - public required string Name { get; set; } + public string Name { get; set; } = string.Empty; [Comment("Описание системы - источника данных")] public string? Description { get; set; } diff --git a/DD.Persistence.Database/Entity/TimestampedValues.cs b/DD.Persistence.Database/Entity/TimestampedValues.cs index 3205203..05c7a57 100644 --- a/DD.Persistence.Database/Entity/TimestampedValues.cs +++ b/DD.Persistence.Database/Entity/TimestampedValues.cs @@ -16,4 +16,7 @@ public class TimestampedValues : ITimestampedItem [Comment("Данные"), Column(TypeName = "jsonb")] public required object[] Values { get; set; } + + [Required, ForeignKey(nameof(DiscriminatorId)), Comment("Идентификаторы")] + public virtual ValuesIdentity? ValuesIdentity { get; set; } } diff --git a/DD.Persistence.Database/Entity/ValuesIdentity.cs b/DD.Persistence.Database/Entity/ValuesIdentity.cs new file mode 100644 index 0000000..dc7f607 --- /dev/null +++ b/DD.Persistence.Database/Entity/ValuesIdentity.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace DD.Persistence.Database.Entity; + +public class ValuesIdentity +{ + [Key, Comment("Дискриминатор системы"),] + public Guid DiscriminatorId { get; set; } + + [Comment("Идентификаторы"), Column(TypeName = "jsonb")] + public string[] Identity { get; set; } = []; +} diff --git a/DD.Persistence.Database/PersistenceDbContext.cs b/DD.Persistence.Database/PersistenceDbContext.cs index 0cddcba..18a01cc 100644 --- a/DD.Persistence.Database/PersistenceDbContext.cs +++ b/DD.Persistence.Database/PersistenceDbContext.cs @@ -11,7 +11,9 @@ public class PersistenceDbContext : DbContext { public DbSet Setpoint => Set(); - public DbSet TimestampedSets => Set(); + public DbSet ValuesIdentities => Set(); + + public DbSet TimestampedValues => Set(); public DbSet ChangeLog => Set(); @@ -29,6 +31,10 @@ public class PersistenceDbContext : DbContext protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Entity() + .Property(e => e.Identity) + .HasJsonConversion(); + modelBuilder.Entity() .Property(e => e.Values) .HasJsonConversion(); diff --git a/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs index a8135c6..97882e5 100644 --- a/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs @@ -212,11 +212,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest }; string jsonString = JsonSerializer.Serialize(t); var values = JsonSerializer.Deserialize(jsonString); - //// - //dynamic obj = new ExpandoObject(); - //// "FieldName" 123 - //obj.FieldName = 123; yield return new TimestampedValuesDto() { diff --git a/DD.Persistence.Repository/DependencyInjection.cs b/DD.Persistence.Repository/DependencyInjection.cs index 492c502..498849a 100644 --- a/DD.Persistence.Repository/DependencyInjection.cs +++ b/DD.Persistence.Repository/DependencyInjection.cs @@ -38,11 +38,14 @@ public static class DependencyInjection services.AddTransient(); services.AddTransient(); - services.AddTransient, TimestampedValuesRepository>(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient, + RelatedDataCachedRepository>(); + services.AddTransient, + RelatedDataCachedRepository>(); - return services; + return services; } } diff --git a/DD.Persistence.Repository/Repositories/DataSourceSystemRepository.cs b/DD.Persistence.Repository/Repositories/DataSourceSystemRepository.cs deleted file mode 100644 index d8b6c0a..0000000 --- a/DD.Persistence.Repository/Repositories/DataSourceSystemRepository.cs +++ /dev/null @@ -1,33 +0,0 @@ -using DD.Persistence.Database.Entity; -using DD.Persistence.Models; -using DD.Persistence.Repositories; -using Mapster; -using Microsoft.EntityFrameworkCore; - -namespace DD.Persistence.Repository.Repositories; -public class DataSourceSystemRepository : IDataSourceSystemRepository -{ - protected DbContext db; - public DataSourceSystemRepository(DbContext db) - { - this.db = db; - } - protected virtual IQueryable GetQueryReadOnly() => db.Set(); - - public virtual async Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token) - { - var entity = dataSourceSystemDto.Adapt(); - - await db.Set().AddAsync(entity, token); - await db.SaveChangesAsync(token); - } - - public virtual async Task> Get(CancellationToken token) - { - var query = GetQueryReadOnly(); - var entities = await query.ToArrayAsync(token); - var dtos = entities.Select(e => e.Adapt()); - - return dtos; - } -} diff --git a/DD.Persistence.Repository/Repositories/RelatedDataRepository.cs b/DD.Persistence.Repository/Repositories/RelatedDataRepository.cs new file mode 100644 index 0000000..7efc329 --- /dev/null +++ b/DD.Persistence.Repository/Repositories/RelatedDataRepository.cs @@ -0,0 +1,33 @@ +using DD.Persistence.Repositories; +using Mapster; +using Microsoft.EntityFrameworkCore; + +namespace DD.Persistence.Repository.Repositories; +public class RelatedDataRepository : IRelatedDataRepository + where TDto : class, new() + where TEntity : class, new() +{ + protected DbContext db; + public RelatedDataRepository(DbContext db) + { + this.db = db; + } + protected virtual IQueryable GetQueryReadOnly() => db.Set(); + + public virtual async Task Add(TDto dataSourceSystemDto, CancellationToken token) + { + var entity = dataSourceSystemDto.Adapt(); + + await db.Set().AddAsync(entity, token); + await db.SaveChangesAsync(token); + } + + public virtual async Task> Get(CancellationToken token) + { + var query = GetQueryReadOnly(); + var entities = await query.ToArrayAsync(token); + var dtos = entities.Select(e => e.Adapt()); + + return dtos; + } +} diff --git a/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs b/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs index 2917021..274a940 100644 --- a/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -1,22 +1,19 @@ -using Mapster; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Caching.Memory; -using Newtonsoft.Json.Linq; using DD.Persistence.Database.Entity; using DD.Persistence.Models; +using DD.Persistence.Models.Common; using DD.Persistence.Models.Requests; using DD.Persistence.Repositories; -using UuidExtensions; -using DD.Persistence.Models.Common; +using Mapster; +using Microsoft.EntityFrameworkCore; namespace DD.Persistence.Repository.Repositories { public class TechMessagesRepository : ITechMessagesRepository { - private readonly IDataSourceSystemRepository sourceSystemRepository; + private readonly IRelatedDataRepository sourceSystemRepository; private DbContext db; - public TechMessagesRepository(DbContext db, IDataSourceSystemRepository sourceSystemRepository) + public TechMessagesRepository(DbContext db, IRelatedDataRepository sourceSystemRepository) { this.db = db; this.sourceSystemRepository = sourceSystemRepository; diff --git a/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs deleted file mode 100644 index 73225be..0000000 --- a/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs +++ /dev/null @@ -1,122 +0,0 @@ -//using Microsoft.EntityFrameworkCore; -//using DD.Persistence.Database.Entity; -//using DD.Persistence.Models; -//using DD.Persistence.Repositories; -//using DD.Persistence.Models.Common; - -//namespace DD.Persistence.Repository.Repositories; - -///// -///// Репозиторий для хранения разных наборов данных временных рядов. -///// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest. -///// idDiscriminator формируют клиенты и только им известно что они обозначают. -///// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено. -///// -//public class TimestampedSetRepository : ITimestampedSetRepository -//{ -// private readonly DbContext db; - -// public TimestampedSetRepository(DbContext db) -// { -// this.db = db; -// } - -// public Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token) -// { -// var entities = sets.Select(set => new TimestampedSet(idDiscriminator, set.Timestamp.ToUniversalTime(), set.Set)); -// var dbSet = db.Set(); -// dbSet.AddRange(entities); -// return db.SaveChangesAsync(token); -// } - -// public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) -// { -// var dbSet = db.Set(); -// var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); - -// if (geTimestamp.HasValue) -// query = ApplyGeTimestamp(query, geTimestamp.Value); - -// query = query -// .OrderBy(item => item.Timestamp) -// .Skip(skip) -// .Take(take); - -// var data = await Materialize(query, token); - -// if (columnNames is not null && columnNames.Any()) -// data = ReduceSetColumnsByNames(data, columnNames); - -// return data; -// } - -// public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) -// { -// var dbSet = db.Set(); -// var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); - -// query = query.OrderByDescending(entity => entity.Timestamp) -// .Take(take) -// .OrderBy(entity => entity.Timestamp); - -// var data = await Materialize(query, token); - -// if (columnNames is not null && columnNames.Any()) -// data = ReduceSetColumnsByNames(data, columnNames); - -// return data; -// } - -// public Task Count(Guid idDiscriminator, CancellationToken token) -// { -// var dbSet = db.Set(); -// var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); -// return query.CountAsync(token); -// } - -// public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) -// { -// var query = db.Set() -// .GroupBy(entity => entity.IdDiscriminator) -// .Select(group => new -// { -// Min = group.Min(entity => entity.Timestamp), -// Max = group.Max(entity => entity.Timestamp), -// }); - -// var item = await query.FirstOrDefaultAsync(token); -// if (item is null) -// return null; - -// return new DatesRangeDto -// { -// From = item.Min, -// To = item.Max, -// }; -// } - -// private static async Task> Materialize(IQueryable query, CancellationToken token) -// { -// var dtoQuery = query.Select(entity => new TimestampedSetDto(entity.Timestamp, entity.Set)); -// var dtos = await dtoQuery.ToArrayAsync(token); -// return dtos; -// } - -// private static IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset geTimestamp) -// { -// var geTimestampUtc = geTimestamp.ToUniversalTime(); -// return query.Where(entity => entity.Timestamp >= geTimestampUtc); -// } - -// private static IEnumerable ReduceSetColumnsByNames(IEnumerable query, IEnumerable columnNames) -// { -// var newQuery = query -// .Select(entity => new TimestampedSetDto( -// entity.Timestamp, -// entity.Set -// .Where(prop => columnNames.Contains(prop.Key)) -// .ToDictionary(prop => prop.Key, prop => prop.Value) -// )); -// return newQuery; -// } -//} diff --git a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs index cc892ad..9be62db 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs @@ -1,24 +1,29 @@ using DD.Persistence.Database.Entity; using DD.Persistence.Models; using DD.Persistence.Models.Common; -using DD.Persistence.ModelsAbstractions; using DD.Persistence.Repositories; +using DD.Persistence.Repository.Extensions; using Mapster; using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json.Linq; +using System.Linq; using System.Text.Json; +using System.Text.Json.Nodes; namespace DD.Persistence.Repository.Repositories; -public class TimestampedValuesRepository : ITimestampedValuesRepository - where TDto : class, ITimestampAbstractDto, new() +public class TimestampedValuesRepository : ITimestampedValuesRepository { private readonly DbContext db; + private readonly IRelatedDataRepository relatedDataRepository; - public TimestampedValuesRepository(DbContext db) + public TimestampedValuesRepository(DbContext db, IRelatedDataRepository relatedDataRepository) { this.db = db; + this.relatedDataRepository = relatedDataRepository; } - protected virtual IQueryable GetQueryReadOnly() => this.db.Set(); + protected virtual IQueryable GetQueryReadOnly() => this.db.Set() + .Include(e => e.ValuesIdentity); public virtual async Task GetDatesRange(Guid discriminatorId, CancellationToken token) { @@ -41,47 +46,57 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository> GetGtDate(Guid discriminatorId, DateTimeOffset date, CancellationToken token) + public virtual async Task> GetGtDate(Guid discriminatorId, DateTimeOffset date, CancellationToken token) { var query = GetQueryReadOnly().Where(e => e.Timestamp > date); var entities = await query.ToArrayAsync(token); - var dtos = entities.Select(e => e.Adapt()); + var dtos = entities.Select(e => e.Adapt()); return dtos; } - public virtual async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) + public virtual async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) { - var entities = dtos - .Select(d => d.Adapt()) - .ToList(); + var timestampedValuesEntities = new List(); + foreach (var dto in dtos) + { + var values = dto.Values + .SelectMany(v => JsonSerializer.Deserialize>(v.ToString()!)!) + .ToDictionary(); - entities.ForEach(d => - { - d.DiscriminatorId = discriminatorId; - d.Timestamp = d.Timestamp.ToUniversalTime(); - }); + var keys = values.Keys.ToArray(); + await CreateValuesIdentityIfNotExist(discriminatorId, keys, token); + + var timestampedValuesEntity = new TimestampedValues() + { + DiscriminatorId = discriminatorId, + Timestamp = dto.Timestamp.ToUniversalTime(), + Values = values.Values.ToArray() + }; + timestampedValuesEntities.Add(timestampedValuesEntity); + } + + await db.Set().AddRangeAsync(timestampedValuesEntities, token); - await db.Set().AddRangeAsync(entities, token); var result = await db.SaveChangesAsync(token); return result; } - protected async Task> GetLastAsync(int takeCount, CancellationToken token) + protected async Task> GetLastAsync(int takeCount, CancellationToken token) { var query = GetQueryReadOnly() .OrderByDescending(e => e.Timestamp) .Take(takeCount); var entities = await query.ToArrayAsync(token); - var dtos = entities.Select(e => e.Adapt()); + var dtos = entities.Select(e => e.Adapt()); return dtos; } - protected async Task GetFirstAsync(CancellationToken token) + protected async Task GetFirstAsync(CancellationToken token) { var query = GetQueryReadOnly() .OrderBy(e => e.Timestamp); @@ -91,11 +106,11 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository(); + var dto = entity.Adapt(); return dto; } - public async virtual Task> GetResampledData( + public async virtual Task> GetResampledData( Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, @@ -116,10 +131,10 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + public async Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) { var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.DiscriminatorId == idDiscriminator); + var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId); if (geTimestamp.HasValue) query = ApplyGeTimestamp(query, geTimestamp.Value); @@ -129,7 +144,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository : ITimestampedValuesRepository> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) + public async Task> GetLast(Guid discriminatorId, IEnumerable? columnNames, int take, CancellationToken token) { var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.DiscriminatorId == idDiscriminator); + var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId); query = query.OrderByDescending(entity => entity.Timestamp) .Take(take) .OrderBy(entity => entity.Timestamp); - var data = await Materialize(query, token); + var data = await Materialize(discriminatorId, query, token); if (columnNames is not null && columnNames.Any()) data = ReduceSetColumnsByNames(data, columnNames); @@ -154,21 +169,43 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository Count(Guid idDiscriminator, CancellationToken token) + public Task Count(Guid discriminatorId, CancellationToken token) { var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.DiscriminatorId == idDiscriminator); + var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId); return query.CountAsync(token); } - private static async Task> Materialize(IQueryable query, CancellationToken token) + private async Task> Materialize(Guid discriminatorId, IQueryable query, CancellationToken token) { - var dtoQuery = query.Select(entity => new TimestampedValuesDto() { Timestamp = entity.Timestamp, Values = entity.Values }); + var dtoQuery = query.Select(entity => new TimestampedValuesDto() + { + Timestamp = entity.Timestamp, + Values = entity.Values + }); + var dtos = await dtoQuery.ToArrayAsync(token); + foreach(var dto in dtos) + { + var valuesIdentities = await relatedDataRepository.Get(token); + var valuesIdentity = valuesIdentities? + .FirstOrDefault(e => e.DiscriminatorId == discriminatorId); + if (valuesIdentity == null) + return []; // ToDo: какая логика должна быть? + + for (var i = 0; i < valuesIdentity.Identity.Count(); i++) + { + var key = valuesIdentity.Identity[i]; + var value = dto.Values[i]; + + dto.Values[i] = new { key = value }; // ToDo: вывод? + } + } + return dtos; } - private static IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset geTimestamp) + private IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset geTimestamp) { var geTimestampUtc = geTimestamp.ToUniversalTime(); return query.Where(entity => entity.Timestamp >= geTimestampUtc); @@ -176,16 +213,43 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository ReduceSetColumnsByNames(IEnumerable query, IEnumerable columnNames) { - var newQuery = query - .Select(entity => new TimestampedValuesDto() - { - Timestamp = entity.Timestamp, - Values = entity.Values? - .Where(prop => columnNames.Contains( - JsonSerializer.Deserialize>(prop.ToString()!)? - .FirstOrDefault().Key - )).ToArray() - }); + var newQuery = query; + //.Select(entity => new TimestampedValuesDto() + //{ + // Timestamp = entity.Timestamp, + // Values = entity.Values? + // .Where(prop => columnNames.Contains( + // JsonSerializer.Deserialize>(prop.ToString()!)? + // .FirstOrDefault().Key + // )).ToArray() + //}); return newQuery; } + + private async Task CreateValuesIdentityIfNotExist(Guid discriminatorId, string[] keys, CancellationToken token) + { + var valuesIdentities = await relatedDataRepository.Get(token); + var valuesIdentity = valuesIdentities? + .FirstOrDefault(e => e.DiscriminatorId == discriminatorId); + + if (valuesIdentity == null) + { + valuesIdentity = new ValuesIdentityDto() + { + DiscriminatorId = discriminatorId, + Identity = keys + }; + await relatedDataRepository.Add(valuesIdentity, token); + + return; + } + + if (!valuesIdentity.Identity.SequenceEqual(keys)) + { + var expectedIdentity = string.Join(", ", valuesIdentity.Identity); + var actualIdentity = string.Join(", ", keys); + throw new InvalidOperationException($"Для системы {discriminatorId.ToString()} " + + $"характерен набор данных: [{expectedIdentity}], однако был передан набор: [{actualIdentity}]"); + } + } } diff --git a/DD.Persistence.Repository/RepositoriesCached/DataSourceSystemCachedRepository.cs b/DD.Persistence.Repository/RepositoriesCached/RelatedDataCachedRepository.cs similarity index 70% rename from DD.Persistence.Repository/RepositoriesCached/DataSourceSystemCachedRepository.cs rename to DD.Persistence.Repository/RepositoriesCached/RelatedDataCachedRepository.cs index febb65e..bc680f0 100644 --- a/DD.Persistence.Repository/RepositoriesCached/DataSourceSystemCachedRepository.cs +++ b/DD.Persistence.Repository/RepositoriesCached/RelatedDataCachedRepository.cs @@ -4,24 +4,26 @@ using DD.Persistence.Models; using DD.Persistence.Repository.Repositories; namespace DD.Persistence.Repository.RepositoriesCached; -public class DataSourceSystemCachedRepository : DataSourceSystemRepository +public class RelatedDataCachedRepository : RelatedDataRepository + where TEntity : class, new() + where TDto : class, new() { private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DataSourceSystem).FullName}CacheKey"; private readonly IMemoryCache memoryCache; private const int CacheExpirationInMinutes = 60; private readonly TimeSpan? AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60); - public DataSourceSystemCachedRepository(DbContext db, IMemoryCache memoryCache) : base(db) + public RelatedDataCachedRepository(DbContext db, IMemoryCache memoryCache) : base(db) { this.memoryCache = memoryCache; } - public override async Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token) + public override async Task Add(TDto dataSourceSystemDto, CancellationToken token) { await base.Add(dataSourceSystemDto, token); memoryCache.Remove(SystemCacheKey); } - public override async Task> Get(CancellationToken token) + public override async Task> Get(CancellationToken token) { var systems = await memoryCache.GetOrCreateAsync(SystemCacheKey, async (cacheEntry) => { diff --git a/DD.Persistence/Models/DataSourceSystemDto.cs b/DD.Persistence/Models/DataSourceSystemDto.cs index 4abe706..2726e91 100644 --- a/DD.Persistence/Models/DataSourceSystemDto.cs +++ b/DD.Persistence/Models/DataSourceSystemDto.cs @@ -13,7 +13,7 @@ public class DataSourceSystemDto /// /// Наименование /// - public required string Name { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; /// /// Описание diff --git a/DD.Persistence/Models/TimestampedValuesDto.cs b/DD.Persistence/Models/TimestampedValuesDto.cs index 938970d..d30b549 100644 --- a/DD.Persistence/Models/TimestampedValuesDto.cs +++ b/DD.Persistence/Models/TimestampedValuesDto.cs @@ -15,5 +15,5 @@ public class TimestampedValuesDto : ITimestampAbstractDto /// /// Набор данных /// - public object[]? Values { get; set; } + public object[] Values { get; set; } = []; } diff --git a/DD.Persistence/Models/ValuesIdentityDto.cs b/DD.Persistence/Models/ValuesIdentityDto.cs new file mode 100644 index 0000000..36d91fc --- /dev/null +++ b/DD.Persistence/Models/ValuesIdentityDto.cs @@ -0,0 +1,17 @@ +namespace DD.Persistence.Models; + +/// +/// Набор идентификаторов для набора данных +/// +public class ValuesIdentityDto +{ + /// + /// Дискриминатор системы + /// + public Guid DiscriminatorId { get; set; } + + /// + /// Идентификаторы + /// + public string[] Identity { get; set; } = []; +} diff --git a/DD.Persistence/Repositories/IDataSourceSystemRepository.cs b/DD.Persistence/Repositories/IDataSourceSystemRepository.cs deleted file mode 100644 index ce674d6..0000000 --- a/DD.Persistence/Repositories/IDataSourceSystemRepository.cs +++ /dev/null @@ -1,22 +0,0 @@ -using DD.Persistence.Models; - -namespace DD.Persistence.Repositories; - -/// -/// Интерфейс по работе с системами - источниками данных -/// -public interface IDataSourceSystemRepository -{ - /// - /// Добавить систему - /// - /// - /// - public Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token); - - /// - /// Получить список систем - /// - /// - public Task> Get(CancellationToken token); -} diff --git a/DD.Persistence/Repositories/IRelatedDataRepository.cs b/DD.Persistence/Repositories/IRelatedDataRepository.cs new file mode 100644 index 0000000..a5f5fbc --- /dev/null +++ b/DD.Persistence/Repositories/IRelatedDataRepository.cs @@ -0,0 +1,23 @@ +using DD.Persistence.Models; + +namespace DD.Persistence.Repositories; + +/// +/// Интерфейс по работе с системами - источниками данных +/// +public interface IRelatedDataRepository +{ + /// + /// Добавить систему + /// + /// + /// + /// + public Task Add(TDto dataSourceSystemDto, CancellationToken token); + + /// + /// Получить список систем + /// + /// + public Task> Get(CancellationToken token); +} diff --git a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs index 5a508c4..72cef69 100644 --- a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs +++ b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs @@ -7,9 +7,7 @@ namespace DD.Persistence.Repositories; /// /// Интерфейс по работе с временными данными /// -/// -public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBaseRepository - where TDto : class, ITimestampAbstractDto, new() +public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBaseRepository { /// /// Добавление записей @@ -18,7 +16,7 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITi /// /// /// - Task AddRange(Guid idDiscriminator, IEnumerable dtos, CancellationToken token); + Task AddRange(Guid idDiscriminator, IEnumerable dtos, CancellationToken token); /// /// Количество записей по указанному набору в БД. Для пагинации. diff --git a/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs b/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs index 4db2645..d60e0ce 100644 --- a/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs +++ b/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs @@ -1,12 +1,12 @@ -using DD.Persistence.Models.Common; +using DD.Persistence.Models; +using DD.Persistence.Models.Common; namespace DD.Persistence.RepositoriesAbstractions; /// /// Интерфейс по работе с данными /// -/// -public interface ISyncRepository +public interface ISyncRepository { /// /// Получить данные, начиная с определенной даты @@ -15,7 +15,7 @@ public interface ISyncRepository /// дата начала /// /// - Task> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token); + Task> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token); /// diff --git a/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs b/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs index 9fd43b4..b8c6791 100644 --- a/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs +++ b/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs @@ -5,7 +5,7 @@ namespace DD.Persistence.RepositoriesAbstractions; /// /// Интерфейс по работе с прореженными данными /// -public interface ITimeSeriesBaseRepository +public interface ITimeSeriesBaseRepository { /// /// Получить список объектов с прореживанием @@ -16,7 +16,7 @@ public interface ITimeSeriesBaseRepository /// /// /// - Task> GetResampledData( + Task> GetResampledData( Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, -- 2.45.2 From fd276f5a43985e066bf36956e3449f7880c17cac Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Fri, 17 Jan 2025 17:21:54 +0500 Subject: [PATCH 06/16] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimestampedValuesController.cs | 125 +++--- .../Clients/Base/BaseClient.cs | 4 +- .../Interfaces/ITimestampedValuesClient.cs | 102 ++--- .../Clients/Interfaces/Refit/IRefitClient.cs | 4 + .../Refit/IRefitTimestampedValuesClient.cs | 45 ++- .../Clients/TimestampedValuesClient.cs | 73 ++-- .../Controllers/TechMessagesControllerTest.cs | 2 +- .../TimestampedSetControllerTest.cs | 224 ----------- .../TimestampedValuesControllerTest.cs | 371 ++++++++++++++++++ .../Extensions/IEnumerableExtensions.cs | 15 + .../TimestampedValuesRepository.cs | 224 +++++------ .../RelatedDataCachedRepository.cs | 8 +- .../TimestampedValuesCachedRepository.cs | 55 ++- DD.Persistence/Models/TimestampedValuesDto.cs | 2 +- .../Repositories/IRelatedDataRepository.cs | 12 +- .../ITimestampedValuesRepository.cs | 31 +- 16 files changed, 771 insertions(+), 526 deletions(-) delete mode 100644 DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs create mode 100644 DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs diff --git a/DD.Persistence.API/Controllers/TimestampedValuesController.cs b/DD.Persistence.API/Controllers/TimestampedValuesController.cs index 2b0b245..c4d498b 100644 --- a/DD.Persistence.API/Controllers/TimestampedValuesController.cs +++ b/DD.Persistence.API/Controllers/TimestampedValuesController.cs @@ -15,26 +15,26 @@ namespace DD.Persistence.API.Controllers; [Route("api/[controller]/{discriminatorId}")] public class TimestampedValuesController : ControllerBase { - private readonly ITimestampedValuesRepository repository; + private readonly ITimestampedValuesRepository timestampedValuesRepository; public TimestampedValuesController(ITimestampedValuesRepository repository) { - this.repository = repository; + this.timestampedValuesRepository = repository; } /// - /// Записать новые данные + /// Записать новые данные. /// Предполагается что данные с одним дискриминатором имеют одинаковую структуру /// /// Дискриминатор (идентификатор) набора - /// + /// /// - /// кол-во затронутых записей [HttpPost] - [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - public async Task AddRange([FromRoute] Guid discriminatorId, [FromBody] IEnumerable sets, CancellationToken token) + [ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)] + public async Task AddRange([FromRoute] Guid discriminatorId, [FromBody] IEnumerable dtos, CancellationToken token) { - var result = await repository.AddRange(discriminatorId, sets, token); + var result = await timestampedValuesRepository.AddRange(discriminatorId, dtos, token); + return Ok(result); } @@ -42,81 +42,100 @@ public class TimestampedValuesController : ControllerBase /// Получение данных с фильтрацией. Значение фильтра null - отключен /// /// Дискриминатор (идентификатор) набора - /// Фильтр позднее даты - /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора + /// Фильтр позднее даты + /// Фильтр свойств набора /// /// /// - /// Фильтрованный набор данных с сортировкой по отметке времени [HttpGet] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task Get([FromRoute] Guid discriminatorId, DateTimeOffset? geTimestamp, [FromQuery] IEnumerable? columnNames, int skip, int take, CancellationToken token) + public async Task>> Get([FromRoute] Guid discriminatorId, DateTimeOffset? timestampBegin, [FromQuery] string[]? columnNames, int skip, int take, CancellationToken token) { - var result = await repository.Get(discriminatorId, geTimestamp, columnNames, skip, take, token); - return Ok(result); + var result = await timestampedValuesRepository.Get(discriminatorId, timestampBegin, columnNames, skip, take, token); + + return result.Any() ? Ok(result) : NoContent(); } /// - /// Получить последние данные + /// Получить данные, начиная с заданной отметки времени + /// + /// Дискриминатор (идентификатор) набора + /// Фильтр позднее даты + /// + [HttpGet("gtdate")] + public async Task>> GetGtDate([FromRoute] Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token) + { + var result = await timestampedValuesRepository.GetGtDate(discriminatorId, timestampBegin, token); + + return result.Any() ? Ok(result) : NoContent(); + } + + /// + /// Получить данные c начала /// /// Дискриминатор (идентификатор) набора - /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора /// /// - /// Фильтрованный набор данных с сортировкой по отметке времени - [HttpGet("last")] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task GetLast([FromRoute] Guid discriminatorId, [FromQuery] IEnumerable? columnNames, int take, CancellationToken token) + [HttpGet("first")] + public async Task>> GetFirst([FromRoute] Guid discriminatorId, int take, CancellationToken token) { - var result = await repository.GetLast(discriminatorId, columnNames, take, token); - return Ok(result); + var result = await timestampedValuesRepository.GetFirst(discriminatorId, take, token); + + return result.Any() ? Ok(result) : NoContent(); } /// - /// Диапазон дат за которые есть данные - /// - /// - /// - /// Дата первой и последней записи - [HttpGet("datesRange")] - [ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - public async Task GetDatesRange([FromRoute] Guid discriminatorId, CancellationToken token) - { - var result = await repository.GetDatesRange(discriminatorId, token); - return Ok(result); - } - - /// - /// Количество записей по указанному набору в БД. Для пагинации. + /// Получить данные c конца /// /// Дискриминатор (идентификатор) набора + /// /// - /// - [HttpGet("count")] - [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - public async Task Count([FromRoute] Guid discriminatorId, CancellationToken token) + [HttpGet("last")] + public async Task>> GetLast([FromRoute] Guid discriminatorId, int take, CancellationToken token) { - var result = await repository.Count(discriminatorId, token); - return Ok(result); + var result = await timestampedValuesRepository.GetLast(discriminatorId, take, token); + + return result.Any() ? Ok(result) : NoContent(); } /// /// Получить список объектов с прореживанием, удовлетворяющий диапазону дат /// - /// - /// + /// Дискриминатор (идентификатор) набора + /// Фильтр позднее даты /// /// /// - /// [HttpGet("resampled")] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - public async Task GetResampledData([FromRoute] Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) + public async Task>> GetResampledData([FromRoute] Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) { - var result = await repository.GetResampledData(discriminatorId, dateBegin, intervalSec, approxPointsCount, token); + var result = await timestampedValuesRepository.GetResampledData(discriminatorId, timestampBegin, intervalSec, approxPointsCount, token); + + return result.Any() ? Ok(result) : NoContent(); + } + + /// + /// Получить количество записей по указанному набору в БД. Для пагинации + /// + /// Дискриминатор (идентификатор) набора + /// + [HttpGet("count")] + public async Task> Count([FromRoute] Guid discriminatorId, CancellationToken token) + { + var result = await timestampedValuesRepository.Count(discriminatorId, token); + + return Ok(result); + } + + /// + /// Получить диапазон дат, в пределах которых хранятся даные + /// + /// + /// + [HttpGet("datesRange")] + public async Task> GetDatesRange([FromRoute] Guid discriminatorId, CancellationToken token) + { + var result = await timestampedValuesRepository.GetDatesRange(discriminatorId, token); + return Ok(result); } } diff --git a/DD.Persistence.Client/Clients/Base/BaseClient.cs b/DD.Persistence.Client/Clients/Base/BaseClient.cs index 3b13cdb..f9bcdd7 100644 --- a/DD.Persistence.Client/Clients/Base/BaseClient.cs +++ b/DD.Persistence.Client/Clients/Base/BaseClient.cs @@ -12,13 +12,13 @@ public abstract class BaseClient this.logger = logger; } - public async Task ExecuteGetResponse(Func>> getMethod, CancellationToken token) + public async Task ExecuteGetResponse(Func>> getMethod, CancellationToken token) { var response = await getMethod.Invoke().WaitAsync(token); if (response.IsSuccessStatusCode) { - return response.Content; + return response.Content!; } var exception = response.GetPersistenceException(); diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs index ea1f576..23f1ddf 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs @@ -10,61 +10,71 @@ namespace DD.Persistence.Client.Clients.Interfaces; /// public interface ITimestampedValuesClient : IDisposable { - /// - /// Записать новые данные - /// - /// - /// - /// - /// - Task AddRange(Guid discriminatorId, IEnumerable sets, CancellationToken token); - - /// - /// Количество записей по указанному набору в БД. Для пагинации - /// - /// - /// - /// - Task Count(Guid discriminatorId, CancellationToken token); - - /// - /// Получение данных с фильтрацией. Значение фильтра null - отключен - /// - /// - /// - /// - /// - /// - /// - /// - Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); - - /// - /// Диапазон дат за которые есть данные - /// - /// - /// - /// - Task GetDatesRange(Guid discriminatorId, CancellationToken token); + /// + /// Записать новые данные + /// Предполагается что данные с одним дискриминатором имеют одинаковую структуру + /// + /// Дискриминатор (идентификатор) набора + /// + /// + Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token); /// - /// Получить последние данные + /// Получить данные с фильтрацией. Значение фильтра null - отключен /// - /// - /// + /// Дискриминатор (идентификатор) набора + /// Фильтр позднее даты + /// Фильтр свойств набора + /// /// /// - /// - Task> GetLast(Guid discriminatorId, IEnumerable? columnNames, int take, CancellationToken token); + Task> Get(Guid discriminatorId, DateTimeOffset? timestampBegin, IEnumerable? columnNames, int skip, int take, CancellationToken token); /// - /// Получить список объектов с прореживанием, удовлетворяющий диапазону дат + /// Получить данные, начиная с заданной отметки времени /// - /// - /// + /// Дискриминатор (идентификатор) набора + /// Фильтр позднее даты + /// + Task> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token); + + /// + /// Получить данные с начала + /// + /// Дискриминатор (идентификатор) набора + /// + /// + Task> GetFirst(Guid discriminatorId, int take, CancellationToken token); + + /// + /// Получить данные с конца + /// + /// Дискриминатор (идентификатор) набора + /// + /// + Task> GetLast(Guid discriminatorId, int take, CancellationToken token); + + /// + /// Получить данные с прореживанием, удовлетворяющем диапазону дат + /// + /// Дискриминатор (идентификатор) набора + /// /// /// /// - /// - Task> GetResampledData(Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); + Task> GetResampledData(Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); + + /// + /// Количество записей по указанному набору в БД. Для пагинации + /// + /// Дискриминатор (идентификатор) набора + /// + Task Count(Guid discriminatorId, CancellationToken token); + + /// + /// Диапазон дат, в пределах которых осуществляется хранение данных + /// + /// Дискриминатор (идентификатор) набора + /// + Task GetDatesRange(Guid discriminatorId, CancellationToken token); } \ No newline at end of file diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitClient.cs index 9568a51..4a140e0 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitClient.cs @@ -5,6 +5,10 @@ using System.Text; using System.Threading.Tasks; namespace DD.Persistence.Client.Clients.Interfaces.Refit; + +/// +/// Базовый Refit интерфейс +/// public interface IRefitClient { } diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs index 2515a57..bd94136 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs @@ -4,25 +4,58 @@ using Refit; namespace DD.Persistence.Client.Clients.Interfaces.Refit; +/// +/// Refit интерфейс для TimestampedValuesController +/// public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable { private const string baseUrl = "/api/TimestampedValues/{discriminatorId}"; + /// + /// Записать новые данные + /// [Post(baseUrl)] - Task> AddRange(Guid discriminatorId, IEnumerable sets, CancellationToken token); + Task> AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token); + /// + /// Получение данных с фильтрацией + /// [Get(baseUrl)] - Task>> Get(Guid discriminatorId, [Query] DateTimeOffset? geTimestamp, [Query] IEnumerable? columnNames, int skip, int take, CancellationToken token); + Task>> Get(Guid discriminatorId, DateTimeOffset? timestampBegin, [Query(CollectionFormat.Multi)] IEnumerable? columnNames, int skip, int take, CancellationToken token); + /// + /// Получить данные, начиная с заданной отметки времени + /// + [Get($"{baseUrl}/gtdate")] + Task>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token); + + /// + /// Получить данные c начала + /// + [Get($"{baseUrl}/first")] + Task>> GetFirst(Guid discriminatorId, int take, CancellationToken token); + + /// + /// Получить данные c конца + /// [Get($"{baseUrl}/last")] - Task>> GetLast(Guid discriminatorId, [Query] IEnumerable? columnNames, int take, CancellationToken token); + Task>> GetLast(Guid discriminatorId, int take, CancellationToken token); + /// + /// Получить список объектов с прореживанием, удовлетворяющий диапазону временных отметок + /// + [Get($"{baseUrl}/resampled")] + Task>> GetResampledData(Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); + + /// + /// Получить количество записей по указанному набору в БД. Для пагинации + /// [Get($"{baseUrl}/count")] Task> Count(Guid discriminatorId, CancellationToken token); + /// + /// Получить диапазон дат, в пределах которых хранятся даные + /// [Get($"{baseUrl}/datesRange")] Task> GetDatesRange(Guid discriminatorId, CancellationToken token); - - [Get($"{baseUrl}/resampled")] - Task>> GetResampledData(Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); } diff --git a/DD.Persistence.Client/Clients/TimestampedValuesClient.cs b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs index d32e9b9..19fce3f 100644 --- a/DD.Persistence.Client/Clients/TimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs @@ -1,20 +1,24 @@ -using Microsoft.Extensions.Logging; -using DD.Persistence.Client.Clients.Base; +using DD.Persistence.Client.Clients.Base; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Models; using DD.Persistence.Models.Common; +using Microsoft.Extensions.Logging; namespace DD.Persistence.Client.Clients; + +/// public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient { private readonly IRefitTimestampedValuesClient refitTimestampedSetClient; + /// public TimestampedValuesClient(IRefitClientFactory refitTimestampedSetClientFactory, ILogger logger) : base(logger) { this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create(); } + /// public async Task AddRange(Guid discriminatorId, IEnumerable sets, CancellationToken token) { var result = await ExecutePostResponse( @@ -23,38 +27,42 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient return result; } + /// public async Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) { var result = await ExecuteGetResponse( async () => await refitTimestampedSetClient.Get(discriminatorId, geTimestamp, columnNames, skip, take, token), token); + return result; + } - return result!; - } + /// + public async Task> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token) + { + var result = await ExecuteGetResponse( + async () => await refitTimestampedSetClient.GetGtDate(discriminatorId, timestampBegin, token), token); - public async Task> GetLast(Guid discriminatorId, IEnumerable? columnNames, int take, CancellationToken token) + return result; + } + + /// + public async Task> GetFirst(Guid discriminatorId, int take, CancellationToken token) + { + var result = await ExecuteGetResponse( + async () => await refitTimestampedSetClient.GetFirst(discriminatorId, take, token), token); + + return result; + } + + /// + public async Task> GetLast(Guid discriminatorId, int take, CancellationToken token) { var result = await ExecuteGetResponse( - async () => await refitTimestampedSetClient.GetLast(discriminatorId, columnNames, take, token), token); + async () => await refitTimestampedSetClient.GetLast(discriminatorId, take, token), token); - return result!; - } - - public async Task Count(Guid discriminatorId, CancellationToken token) - { - var result = await ExecuteGetResponse( - async () => await refitTimestampedSetClient.Count(discriminatorId, token), token); - - return result; - } - - public async Task GetDatesRange(Guid discriminatorId, CancellationToken token) - { - var result = await ExecuteGetResponse( - async () => await refitTimestampedSetClient.GetDatesRange(discriminatorId, token), token); - - return result; + return result; } + /// public async Task> GetResampledData(Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default) { var result = await ExecuteGetResponse( @@ -63,6 +71,25 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient return result; } + /// + public async Task Count(Guid discriminatorId, CancellationToken token) + { + var result = await ExecuteGetResponse( + async () => await refitTimestampedSetClient.Count(discriminatorId, token), token); + + return result; + } + + /// + public async Task GetDatesRange(Guid discriminatorId, CancellationToken token) + { + var result = await ExecuteGetResponse( + async () => await refitTimestampedSetClient.GetDatesRange(discriminatorId, token), token); + + return result; + } + + /// public void Dispose() { refitTimestampedSetClient.Dispose(); diff --git a/DD.Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs index 51afc02..7974470 100644 --- a/DD.Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs @@ -15,7 +15,7 @@ namespace DD.Persistence.IntegrationTests.Controllers { public class TechMessagesControllerTest : BaseIntegrationTest { - private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DataSourceSystem).FullName}CacheKey"; + private static readonly string SystemCacheKey = $"{typeof(DataSourceSystem).FullName}CacheKey"; private readonly ITechMessagesClient techMessagesClient; private readonly IMemoryCache memoryCache; public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory) diff --git a/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs deleted file mode 100644 index 97882e5..0000000 --- a/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs +++ /dev/null @@ -1,224 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using DD.Persistence.Client; -using DD.Persistence.Client.Clients.Interfaces; -using DD.Persistence.Models; -using Xunit; -using DD.Persistence.Client.Clients.Interfaces.Refit; -using DD.Persistence.Client.Clients; -using Microsoft.Extensions.Logging; -using System.Text.Json; -using System.Text; -using Microsoft.Extensions.Primitives; -using System.Dynamic; -using Newtonsoft.Json.Linq; - -namespace DD.Persistence.IntegrationTests.Controllers; -public class TimestampedSetControllerTest : BaseIntegrationTest -{ - private readonly ITimestampedValuesClient client; - - public TimestampedSetControllerTest(WebAppFactoryFixture factory) : base(factory) - { - var refitClientFactory = scope.ServiceProvider - .GetRequiredService>(); - var logger = scope.ServiceProvider.GetRequiredService>(); - - client = scope.ServiceProvider - .GetRequiredService(); - } - - [Fact] - public async Task InsertRange() - { - // arrange - Guid idDiscriminator = Guid.NewGuid(); - IEnumerable testSets = Generate(10, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - - // act - var response = await client.AddRange(idDiscriminator, testSets, CancellationToken.None); - - // assert - Assert.Equal(testSets.Count(), response); - } - - [Fact] - public async Task Get_without_filter() - { - // arrange - Guid idDiscriminator = Guid.NewGuid(); - int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - await client.AddRange(idDiscriminator, testSets, CancellationToken.None); - - // act - var response = await client.Get(idDiscriminator, null, null, 0, int.MaxValue, CancellationToken.None); - - // assert - Assert.NotNull(response); - Assert.Equal(count, response.Count()); - } - - [Fact] - public async Task Get_with_filter_props() - { - // arrange - Guid idDiscriminator = Guid.NewGuid(); - int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - await client.AddRange(idDiscriminator, testSets, CancellationToken.None); - string[] props = ["A"]; - - // act - var response = await client.Get(idDiscriminator, null, props, 0, int.MaxValue, new CancellationToken()); - - // assert - Assert.NotNull(response); - Assert.Equal(count, response.Count()); - foreach (var item in response) - { - Assert.Single(item.Values!); - var kv = item.Values!.First(); - Assert.Equal("A", ((KeyValuePair) kv).Key); - } - } - - [Fact] - public async Task Get_geDate() - { - // arrange - Guid idDiscriminator = Guid.NewGuid(); - int count = 10; - var dateMin = DateTimeOffset.Now; - var dateMax = DateTimeOffset.Now.AddSeconds(count); - IEnumerable testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); - var insertResponse = await client.AddRange(idDiscriminator, testSets, CancellationToken.None); - var tail = testSets.OrderBy(t => t.Timestamp).Skip(count / 2).Take(int.MaxValue); - var geDate = tail.First().Timestamp; - var tolerance = TimeSpan.FromSeconds(1); - var expectedCount = tail.Count(); - - // act - var response = await client.Get(idDiscriminator, geDate, null, 0, int.MaxValue, CancellationToken.None); - - // assert - Assert.NotNull(response); - Assert.Equal(expectedCount, response.Count()); - var minDate = response.Min(t => t.Timestamp); - Assert.Equal(geDate, geDate, tolerance); - } - - [Fact] - public async Task Get_with_skip_take() - { - // arrange - Guid idDiscriminator = Guid.NewGuid(); - int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - await client.AddRange(idDiscriminator, testSets, CancellationToken.None); - var expectedCount = count / 2; - - // act - var response = await client.Get(idDiscriminator, null, null, 2, expectedCount, new CancellationToken()); - - // assert - Assert.NotNull(response); - Assert.Equal(expectedCount, response.Count()); - } - - - [Fact] - public async Task Get_with_big_skip_take() - { - // arrange - Guid idDiscriminator = Guid.NewGuid(); - var expectedCount = 1; - int count = 10 + expectedCount; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - await client.AddRange(idDiscriminator, testSets, CancellationToken.None); - - // act - var response = await client.Get(idDiscriminator, null, null, count - expectedCount, count, new CancellationToken()); - - // assert - Assert.NotNull(response); - Assert.Equal(expectedCount, response.Count()); - } - - [Fact] - public async Task GetLast() - { - // arrange - Guid idDiscriminator = Guid.NewGuid(); - int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - await client.AddRange(idDiscriminator, testSets, CancellationToken.None); - var expectedCount = 8; - - // act - var response = await client.GetLast(idDiscriminator, null, expectedCount, new CancellationToken()); - - // assert - Assert.NotNull(response); - Assert.Equal(expectedCount, response.Count()); - } - - [Fact] - public async Task GetDatesRange() - { - // arrange - Guid idDiscriminator = Guid.NewGuid(); - int count = 10; - var dateMin = DateTimeOffset.Now; - var dateMax = DateTimeOffset.Now.AddSeconds(count - 1); - IEnumerable testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); - await client.AddRange(idDiscriminator, testSets, CancellationToken.None); - var tolerance = TimeSpan.FromSeconds(1); - - // act - var response = await client.GetDatesRange(idDiscriminator, new CancellationToken()); - - // assert - Assert.NotNull(response); - Assert.Equal(dateMin, response.From, tolerance); - Assert.Equal(dateMax, response.To, tolerance); - } - - [Fact] - public async Task Count() - { - // arrange - Guid idDiscriminator = Guid.NewGuid(); - int count = 144; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - await client.AddRange(idDiscriminator, testSets, CancellationToken.None); - - // act - var response = await client.Count(idDiscriminator, new CancellationToken()); - - // assert - Assert.Equal(count, response); - } - - private static IEnumerable Generate(int n, DateTimeOffset from) - { - var result = new List(); - for (int i = 0; i < n; i++) - { - var t = new object[] { - new { A = i }, - new { B = i * 1.1 }, - new { C = $"Any{i}" }, - new { D = DateTimeOffset.Now } - }; - string jsonString = JsonSerializer.Serialize(t); - var values = JsonSerializer.Deserialize(jsonString); - - - yield return new TimestampedValuesDto() - { - Timestamp = from.AddSeconds(i), - Values = values - }; - } - } -} diff --git a/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs new file mode 100644 index 0000000..b166631 --- /dev/null +++ b/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs @@ -0,0 +1,371 @@ +using DD.Persistence.Client; +using DD.Persistence.Client.Clients; +using DD.Persistence.Client.Clients.Interfaces; +using DD.Persistence.Client.Clients.Interfaces.Refit; +using DD.Persistence.Database.Entity; +using DD.Persistence.Models; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System.Text.Json; +using Xunit; + +namespace DD.Persistence.IntegrationTests.Controllers; +public class TimestampedValuesControllerTest : BaseIntegrationTest +{ + private static readonly string SystemCacheKey = $"{typeof(ValuesIdentity).FullName}CacheKey"; + private readonly ITimestampedValuesClient timestampedValuesClient; + private readonly IMemoryCache memoryCache; + + public TimestampedValuesControllerTest(WebAppFactoryFixture factory) : base(factory) + { + var refitClientFactory = scope.ServiceProvider + .GetRequiredService>(); + var logger = scope.ServiceProvider.GetRequiredService>(); + + timestampedValuesClient = scope.ServiceProvider + .GetRequiredService(); + memoryCache = scope.ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task AddRange_returns_success() + { + var discriminatorId = Guid.NewGuid(); + + await AddRange(discriminatorId); + } + + [Fact] + public async Task Get_returns_success() + { + //arrange + Cleanup(); + + var discriminatorId = Guid.NewGuid(); + + //act + var response = await timestampedValuesClient.Get(discriminatorId, null, null, 0, 1, CancellationToken.None); + + //assert + Assert.Null(response); + } + + [Fact] + + public async Task Get_AfterSave_returns_success() + { + //arrange + Cleanup(); + + var discriminatorId = Guid.NewGuid(); + var timestampBegin = DateTimeOffset.UtcNow.AddDays(-1); + var columnNames = new List() { "A", "C" }; + var skip = 5; + var take = 5; + + var dtos = await AddRange(discriminatorId); + + //act + var response = await timestampedValuesClient.Get(discriminatorId, timestampBegin, columnNames, skip, take, CancellationToken.None); + + //assert + Assert.NotNull(response); + Assert.NotEmpty(response); + + var actualCount = response.Count(); + Assert.Equal(take, actualCount); + + var actualColumnNames = response.SelectMany(e => e.Values.Keys).Distinct().ToList(); + Assert.Equal(columnNames, actualColumnNames); + + var expectedValueKind = JsonValueKind.Number; + var actualValueKind = ((JsonElement) response.First().Values["A"]).ValueKind; + Assert.Equal(expectedValueKind, actualValueKind); + + expectedValueKind = JsonValueKind.String; + actualValueKind = ((JsonElement)response.First().Values["C"]).ValueKind; + Assert.Equal(expectedValueKind, actualValueKind); + } + + [Fact] + public async Task GetGtDate_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + var timestampBegin = DateTimeOffset.UtcNow.AddDays(-1); + + //act + var response = await timestampedValuesClient.GetGtDate(discriminatorId, timestampBegin, CancellationToken.None); + + //assert + Assert.Null(response); + } + + [Fact] + public async Task GetGtDate_AfterSave_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + var dtos = await AddRange(discriminatorId); + var timestampBegin = DateTimeOffset.UtcNow.AddSeconds(-5); + + //act + var response = await timestampedValuesClient.GetGtDate(discriminatorId, timestampBegin, CancellationToken.None); + + //assert + Assert.NotNull(response); + Assert.NotEmpty(response); + + var expectedCount = dtos.Count(dto => dto.Timestamp.ToUniversalTime() > timestampBegin); + var actualCount = response.Count(); + Assert.Equal(expectedCount, actualCount); + } + + [Fact] + public async Task GetFirst_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + var take = 1; + + //act + var response = await timestampedValuesClient.GetFirst(discriminatorId, take, CancellationToken.None); + + //assert + Assert.Null(response); + } + + [Fact] + public async Task GetFirst_AfterSave_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + var dtos = await AddRange(discriminatorId); + var take = 1; + + //act + var response = await timestampedValuesClient.GetFirst(discriminatorId, take, CancellationToken.None); + + //assert + Assert.NotNull(response); + Assert.NotEmpty(response); + + var expectedTimestampString = dtos + .OrderBy(dto => dto.Timestamp) + .First().Timestamp + .ToUniversalTime() + .ToString(); + var actualTimestampString = response + .First().Timestamp + .ToUniversalTime() + .ToString(); + Assert.Equal(expectedTimestampString, actualTimestampString); + } + + [Fact] + public async Task GetLast_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + var take = 1; + + //act + var response = await timestampedValuesClient.GetLast(discriminatorId, take, CancellationToken.None); + + //assert + Assert.Null(response); + } + + [Fact] + public async Task GetLast_AfterSave_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + var dtos = await AddRange(discriminatorId); + var take = 1; + + //act + var response = await timestampedValuesClient.GetLast(discriminatorId, take, CancellationToken.None); + + //assert + Assert.NotNull(response); + Assert.NotEmpty(response); + + var expectedTimestampString = dtos + .OrderByDescending(dto => dto.Timestamp) + .First().Timestamp + .ToUniversalTime() + .ToString(); + var actualTimestampString = response + .First().Timestamp + .ToUniversalTime() + .ToString(); + Assert.Equal(expectedTimestampString, actualTimestampString); + } + + [Fact] + public async Task GetResampledData_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + var timestampBegin = DateTimeOffset.UtcNow; + + //act + var response = await timestampedValuesClient.GetResampledData(discriminatorId, timestampBegin); + + //assert + Assert.Null(response); + } + + [Fact] + public async Task GetResampledData_AfterSave_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + var count = 2048; + var timestampBegin = DateTimeOffset.UtcNow; + var dtos = await AddRange(discriminatorId, count); + + + //act + var response = await timestampedValuesClient.GetResampledData(discriminatorId, timestampBegin, count); + + //assert + Assert.NotNull(response); + Assert.NotEmpty(response); + + var expectedCount = count / 2; + var actualCount = response.Count(); + Assert.Equal(expectedCount, actualCount); + } + + [Fact] + public async Task Count_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + + //act + var response = await timestampedValuesClient.Count(discriminatorId, CancellationToken.None); + + //assert + Assert.Equal(0, response); + } + + [Fact] + public async Task Count_AfterSave_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + var dtos = await AddRange(discriminatorId); + + //act + var response = await timestampedValuesClient.Count(discriminatorId, CancellationToken.None); + + //assert + var expectedCount = dtos.Count(); + Assert.Equal(expectedCount, response); + } + + [Fact] + public async Task GetDatesRange_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + + //act + var response = await timestampedValuesClient.GetDatesRange(discriminatorId, CancellationToken.None); + + //assert + Assert.Null(response); + } + + [Fact] + public async Task GetDatesRange_AfterSave_returns_success() + { + //arrange + Cleanup(); + var discriminatorId = Guid.NewGuid(); + var dtos = await AddRange(discriminatorId); + + //act + var response = await timestampedValuesClient.GetDatesRange(discriminatorId, CancellationToken.None); + + //assert + Assert.NotNull(response); + + var expectedDateFromString = dtos + .OrderBy(dto => dto.Timestamp) + .First().Timestamp + .ToUniversalTime() + .ToString(); + var actualDateFromString = response.From + .ToUniversalTime() + .ToString(); + Assert.Equal(expectedDateFromString, actualDateFromString); + + var expectedDateToString = dtos + .OrderByDescending(dto => dto.Timestamp) + .First().Timestamp + .ToUniversalTime() + .ToString(); + var actualDateToString = response.To + .ToUniversalTime() + .ToString(); + Assert.Equal(expectedDateToString, actualDateToString); + } + + private async Task> AddRange(Guid discriminatorId, int countToCreate = 10) + { + // arrange + IEnumerable generatedDtos = Generate(countToCreate, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + + // act + var response = await timestampedValuesClient.AddRange(discriminatorId, generatedDtos, CancellationToken.None); + + // assert + Assert.Equal(generatedDtos.Count(), response); + + return generatedDtos; + } + + private static IEnumerable Generate(int countToCreate, DateTimeOffset from) + { + var result = new List(); + for (int i = 0; i < countToCreate; i++) + { + var values = new Dictionary() + { + { "A", i }, + { "B", i * 1.1 }, + { "C", $"Any{i}" }, + { "D", DateTimeOffset.Now }, + }; + + yield return new TimestampedValuesDto() + { + Timestamp = from.AddSeconds(i), + Values = values + }; + } + } + + private void Cleanup() + { + memoryCache.Remove(SystemCacheKey); + dbContext.CleanupDbSet(); + dbContext.CleanupDbSet(); + } +} diff --git a/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs b/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs index 270107f..e307513 100644 --- a/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs +++ b/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs @@ -14,4 +14,19 @@ public static class IEnumerableExtensions action(item); } } + + public static bool IsNullOrEmpty(this IEnumerable? enumerable) + { + if (enumerable == null) + { + return true; + } + + var collection = enumerable as ICollection; + if (collection != null) + { + return collection.Count < 1; + } + return !enumerable.Any(); + } } diff --git a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs index 9be62db..9e1597c 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs @@ -3,12 +3,7 @@ using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.Repositories; using DD.Persistence.Repository.Extensions; -using Mapster; using Microsoft.EntityFrameworkCore; -using Newtonsoft.Json.Linq; -using System.Linq; -using System.Text.Json; -using System.Text.Json.Nodes; namespace DD.Persistence.Repository.Repositories; public class TimestampedValuesRepository : ITimestampedValuesRepository @@ -25,54 +20,19 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository protected virtual IQueryable GetQueryReadOnly() => this.db.Set() .Include(e => e.ValuesIdentity); - public virtual async Task GetDatesRange(Guid discriminatorId, CancellationToken token) - { - var query = GetQueryReadOnly() - .GroupBy(entity => entity.DiscriminatorId) - .Select(group => new - { - Min = group.Min(entity => entity.Timestamp), - Max = group.Max(entity => entity.Timestamp), - }); - - var item = await query.FirstOrDefaultAsync(token); - if (item is null) - return null; - - return new DatesRangeDto - { - From = item.Min, - To = item.Max, - }; - } - - public virtual async Task> GetGtDate(Guid discriminatorId, DateTimeOffset date, CancellationToken token) - { - var query = GetQueryReadOnly().Where(e => e.Timestamp > date); - var entities = await query.ToArrayAsync(token); - - var dtos = entities.Select(e => e.Adapt()); - - return dtos; - } - - public virtual async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) + public async virtual Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) { var timestampedValuesEntities = new List(); foreach (var dto in dtos) { - var values = dto.Values - .SelectMany(v => JsonSerializer.Deserialize>(v.ToString()!)!) - .ToDictionary(); - - var keys = values.Keys.ToArray(); + var keys = dto.Values.Keys.ToArray(); await CreateValuesIdentityIfNotExist(discriminatorId, keys, token); var timestampedValuesEntity = new TimestampedValues() { DiscriminatorId = discriminatorId, Timestamp = dto.Timestamp.ToUniversalTime(), - Values = values.Values.ToArray() + Values = dto.Values.Values.ToArray() }; timestampedValuesEntities.Add(timestampedValuesEntity); } @@ -84,32 +44,54 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository return result; } - protected async Task> GetLastAsync(int takeCount, CancellationToken token) + public async virtual Task> Get(Guid discriminatorId, DateTimeOffset? timestampBegin, IEnumerable? columnNames, int skip, int take, CancellationToken token) + { + var query = GetQueryReadOnly() + .Where(entity => entity.DiscriminatorId == discriminatorId); + + // Фильтрация по дате + if (timestampBegin.HasValue) + { + query = ApplyGeTimestamp(query, timestampBegin.Value); + } + + query = query + .OrderBy(item => item.Timestamp) + .Skip(skip) + .Take(take); + var data = await Materialize(discriminatorId, query, token); + + // Фильтрация по запрашиваемым полям + if (!columnNames.IsNullOrEmpty()) + { + data = ReduceSetColumnsByNames(data, columnNames!); + } + + return data; + } + + public async virtual Task> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token) + { + var query = GetQueryReadOnly() + .OrderBy(e => e.Timestamp) + .Take(takeCount); + + var dtos = await Materialize(discriminatorId, query, token); + + return dtos; + } + + public async virtual Task> GetLast(Guid discriminatorId, int takeCount, CancellationToken token) { var query = GetQueryReadOnly() .OrderByDescending(e => e.Timestamp) .Take(takeCount); - var entities = await query.ToArrayAsync(token); - var dtos = entities.Select(e => e.Adapt()); + var dtos = await Materialize(discriminatorId, query, token); return dtos; } - protected async Task GetFirstAsync(CancellationToken token) - { - var query = GetQueryReadOnly() - .OrderBy(e => e.Timestamp); - - var entity = await query.FirstOrDefaultAsync(token); - - if (entity == null) - return null; - - var dto = entity.Adapt(); - return dto; - } - public async virtual Task> GetResampledData( Guid discriminatorId, DateTimeOffset dateBegin, @@ -131,99 +113,103 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository return dtos; } - public async Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + public async virtual Task> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token) { - var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId); + var query = GetQueryReadOnly() + .Where(e => e.Timestamp > timestampBegin); - if (geTimestamp.HasValue) - query = ApplyGeTimestamp(query, geTimestamp.Value); + var dtos = await Materialize(discriminatorId, query, token); - query = query - .OrderBy(item => item.Timestamp) - .Skip(skip) - .Take(take); - - var data = await Materialize(discriminatorId, query, token); - - if (columnNames is not null && columnNames.Any()) - data = ReduceSetColumnsByNames(data, columnNames); - - return data; + return dtos; } - public async Task> GetLast(Guid discriminatorId, IEnumerable? columnNames, int take, CancellationToken token) + public async virtual Task GetDatesRange(Guid discriminatorId, CancellationToken token) { - var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId); + var query = GetQueryReadOnly() + .GroupBy(entity => entity.DiscriminatorId) + .Select(group => new + { + Min = group.Min(entity => entity.Timestamp), + Max = group.Max(entity => entity.Timestamp), + }); - query = query.OrderByDescending(entity => entity.Timestamp) - .Take(take) - .OrderBy(entity => entity.Timestamp); + var item = await query.FirstOrDefaultAsync(token); + if (item is null) + { + return null; + } - var data = await Materialize(discriminatorId, query, token); + var dto = new DatesRangeDto + { + From = item.Min, + To = item.Max, + }; - if (columnNames is not null && columnNames.Any()) - data = ReduceSetColumnsByNames(data, columnNames); - - return data; + return dto; } - public Task Count(Guid discriminatorId, CancellationToken token) + public virtual Task Count(Guid discriminatorId, CancellationToken token) { var dbSet = db.Set(); var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId); + return query.CountAsync(token); } private async Task> Materialize(Guid discriminatorId, IQueryable query, CancellationToken token) { - var dtoQuery = query.Select(entity => new TimestampedValuesDto() - { - Timestamp = entity.Timestamp, - Values = entity.Values - }); + var valuesIdentities = await relatedDataRepository.Get(token); + var valuesIdentity = valuesIdentities? + .FirstOrDefault(e => e.DiscriminatorId == discriminatorId); + if (valuesIdentity == null) + return []; - var dtos = await dtoQuery.ToArrayAsync(token); - foreach(var dto in dtos) + var entities = await query.ToArrayAsync(token); + + var dtos = entities.Select(entity => { - var valuesIdentities = await relatedDataRepository.Get(token); - var valuesIdentity = valuesIdentities? - .FirstOrDefault(e => e.DiscriminatorId == discriminatorId); - if (valuesIdentity == null) - return []; // ToDo: какая логика должна быть? + var dto = new TimestampedValuesDto() + { + Timestamp = entity.Timestamp.ToUniversalTime() + }; for (var i = 0; i < valuesIdentity.Identity.Count(); i++) { var key = valuesIdentity.Identity[i]; - var value = dto.Values[i]; + var value = entity.Values[i]; - dto.Values[i] = new { key = value }; // ToDo: вывод? + dto.Values.Add(key, value); } - } + + return dto; + }); return dtos; } - private IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset geTimestamp) + private IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset timestampBegin) { - var geTimestampUtc = geTimestamp.ToUniversalTime(); - return query.Where(entity => entity.Timestamp >= geTimestampUtc); + var geTimestampUtc = timestampBegin.ToUniversalTime(); + + var result = query + .Where(entity => entity.Timestamp >= geTimestampUtc); + + return result; } - private static IEnumerable ReduceSetColumnsByNames(IEnumerable query, IEnumerable columnNames) + private IEnumerable ReduceSetColumnsByNames(IEnumerable dtos, IEnumerable columnNames) { - var newQuery = query; - //.Select(entity => new TimestampedValuesDto() - //{ - // Timestamp = entity.Timestamp, - // Values = entity.Values? - // .Where(prop => columnNames.Contains( - // JsonSerializer.Deserialize>(prop.ToString()!)? - // .FirstOrDefault().Key - // )).ToArray() - //}); - return newQuery; + var result = dtos.Select(dto => + { + var reducedValues = dto.Values + .Where(v => columnNames.Contains(v.Key)) + .ToDictionary(); + dto.Values = reducedValues; + + return dto; + }); + + return result; } private async Task CreateValuesIdentityIfNotExist(Guid discriminatorId, string[] keys, CancellationToken token) @@ -232,7 +218,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository var valuesIdentity = valuesIdentities? .FirstOrDefault(e => e.DiscriminatorId == discriminatorId); - if (valuesIdentity == null) + if (valuesIdentity is null) { valuesIdentity = new ValuesIdentityDto() { diff --git a/DD.Persistence.Repository/RepositoriesCached/RelatedDataCachedRepository.cs b/DD.Persistence.Repository/RepositoriesCached/RelatedDataCachedRepository.cs index bc680f0..6f596ba 100644 --- a/DD.Persistence.Repository/RepositoriesCached/RelatedDataCachedRepository.cs +++ b/DD.Persistence.Repository/RepositoriesCached/RelatedDataCachedRepository.cs @@ -1,16 +1,14 @@ -using Microsoft.EntityFrameworkCore; +using DD.Persistence.Repository.Repositories; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; -using DD.Persistence.Models; -using DD.Persistence.Repository.Repositories; namespace DD.Persistence.Repository.RepositoriesCached; public class RelatedDataCachedRepository : RelatedDataRepository where TEntity : class, new() where TDto : class, new() { - private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DataSourceSystem).FullName}CacheKey"; + private static readonly string SystemCacheKey = $"{typeof(TEntity).FullName}CacheKey"; private readonly IMemoryCache memoryCache; - private const int CacheExpirationInMinutes = 60; private readonly TimeSpan? AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60); public RelatedDataCachedRepository(DbContext db, IMemoryCache memoryCache) : base(db) diff --git a/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs b/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs index d1eba07..7bdaa98 100644 --- a/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs +++ b/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs @@ -1,43 +1,41 @@ -//using Microsoft.EntityFrameworkCore; +//using DD.Persistence.Models; //using DD.Persistence.Models.Common; -//using DD.Persistence.ModelsAbstractions; -//using DD.Persistence.Database.EntityAbstractions; +//using DD.Persistence.Repositories; +//using Microsoft.EntityFrameworkCore; //namespace DD.Persistence.Repository.Repositories; -//public class TimestampedValuesCachedRepository : TimeSeriesDataRepository -// where TEntity : class, ITimestampedItem, new() -// where TDto : class, ITimestampAbstractDto, new() +//public class TimestampedValuesCachedRepository : TimestampedValuesRepository //{ -// public static TDto? FirstByDate { get; private set; } -// public static CyclicArray LastData { get; } = new CyclicArray(CacheItemsCount); +// public static TimestampedValuesDto? FirstByDate { get; private set; } +// public static CyclicArray LastData { get; } = new CyclicArray(CacheItemsCount); // private const int CacheItemsCount = 3600; -// public TimestampedValuesCachedRepository(DbContext db) : base(db) +// public TimestampedValuesCachedRepository(DbContext db, IRelatedDataRepository relatedDataRepository) : base(db, relatedDataRepository) // { -// Task.Run(async () => -// { -// var firstDateItem = await base.GetFirstAsync(CancellationToken.None); -// if (firstDateItem == null) -// { -// return; -// } +// //Task.Run(async () => +// //{ +// // var firstDateItem = await base.GetFirst(CancellationToken.None); +// // if (firstDateItem == null) +// // { +// // return; +// // } -// FirstByDate = firstDateItem; +// // FirstByDate = firstDateItem; -// var dtos = await base.GetLastAsync(CacheItemsCount, CancellationToken.None); -// dtos = dtos.OrderBy(d => d.Timestamp); -// LastData.AddRange(dtos); -// }).Wait(); +// // var dtos = await base.GetLast(CacheItemsCount, CancellationToken.None); +// // dtos = dtos.OrderBy(d => d.Timestamp); +// // LastData.AddRange(dtos); +// //}).Wait(); // } -// public override async Task> GetGtDate(DateTimeOffset dateBegin, CancellationToken token) +// public override async Task> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token) // { // if (LastData.Count == 0 || LastData[0].Timestamp > dateBegin) // { -// var dtos = await base.GetGtDate(dateBegin, token); +// var dtos = await base.GetGtDate(discriminatorId, dateBegin, token); // return dtos; // } @@ -47,9 +45,9 @@ // return items; // } -// public override async Task AddRange(IEnumerable dtos, CancellationToken token) +// public override async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) // { -// var result = await base.AddRange(dtos, token); +// var result = await base.AddRange(discriminatorId, dtos, token); // if (result > 0) // { @@ -62,7 +60,7 @@ // return result; // } -// public override async Task GetDatesRange(CancellationToken token) +// public override async Task GetDatesRange(Guid discriminatorId, CancellationToken token) // { // if (FirstByDate == null) // return null; @@ -77,7 +75,8 @@ // }); // } -// public override async Task> GetResampledData( +// public override async Task> GetResampledData( +// Guid discriminatorId, // DateTimeOffset dateBegin, // double intervalSec = 600d, // int approxPointsCount = 1024, @@ -86,7 +85,7 @@ // var dtos = LastData.Where(i => i.Timestamp >= dateBegin); // if (LastData.Count == 0 || LastData[0].Timestamp > dateBegin) // { -// dtos = await base.GetGtDate(dateBegin, token); +// dtos = await base.GetGtDate(discriminatorId, dateBegin, token); // } // var dateEnd = dateBegin.AddSeconds(intervalSec); diff --git a/DD.Persistence/Models/TimestampedValuesDto.cs b/DD.Persistence/Models/TimestampedValuesDto.cs index d30b549..13b592e 100644 --- a/DD.Persistence/Models/TimestampedValuesDto.cs +++ b/DD.Persistence/Models/TimestampedValuesDto.cs @@ -15,5 +15,5 @@ public class TimestampedValuesDto : ITimestampAbstractDto /// /// Набор данных /// - public object[] Values { get; set; } = []; + public Dictionary Values { get; set; } = []; } diff --git a/DD.Persistence/Repositories/IRelatedDataRepository.cs b/DD.Persistence/Repositories/IRelatedDataRepository.cs index a5f5fbc..e7eae87 100644 --- a/DD.Persistence/Repositories/IRelatedDataRepository.cs +++ b/DD.Persistence/Repositories/IRelatedDataRepository.cs @@ -1,14 +1,14 @@ -using DD.Persistence.Models; - -namespace DD.Persistence.Repositories; +namespace DD.Persistence.Repositories; /// -/// Интерфейс по работе с системами - источниками данных +/// Интерфейс по работе с простой структурой данных, подразумевающей наличие связи с более сложной +/// В контексте TechMessagesRepository это системы - источники данных +/// В контексте TimestampedValuesRepository это идентификационные наборы (ключи для значений в соответствии с индексами в хранимых массивах) /// public interface IRelatedDataRepository { /// - /// Добавить систему + /// Добавить данные /// /// /// @@ -16,7 +16,7 @@ public interface IRelatedDataRepository public Task Add(TDto dataSourceSystemDto, CancellationToken token); /// - /// Получить список систем + /// Получить список данных /// /// public Task> Get(CancellationToken token); diff --git a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs index 72cef69..abb860c 100644 --- a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs +++ b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs @@ -1,5 +1,4 @@ using DD.Persistence.Models; -using DD.Persistence.ModelsAbstractions; using DD.Persistence.RepositoriesAbstractions; namespace DD.Persistence.Repositories; @@ -19,23 +18,13 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase Task AddRange(Guid idDiscriminator, IEnumerable dtos, CancellationToken token); /// - /// Количество записей по указанному набору в БД. Для пагинации. + /// Количество записей по указанному набору в БД. Для пагинации /// /// Дискриминатор (идентификатор) набора /// /// Task Count(Guid idDiscriminator, CancellationToken token); - /// - /// Получить последние данные - /// - /// Дискриминатор (идентификатор) набора - /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора - /// - /// - /// - Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token); - /// /// Получение данных с фильтрацией. Значение фильтра null - отключен /// @@ -47,4 +36,22 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase /// /// Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); + + /// + /// Получение данных с начала + /// + /// Дискриминатор (идентификатор) набора + /// Количество + /// + /// + Task> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token); + + /// + /// Получение данных с конца + /// + /// Дискриминатор (идентификатор) набора + /// Количество + /// + /// + Task> GetLast(Guid discriminatorId, int takeCount, CancellationToken token); } -- 2.45.2 From c9405bf64a6837766a7a3a4536c68c52d45ea4c6 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Fri, 17 Jan 2025 17:36:51 +0500 Subject: [PATCH 07/16] Merge branch 'master' into TimestampedValuesRepository --- .../Refit/IRefitTechMessagesClient.cs | 3 +- .../Interfaces/Refit/IRefitWitsDataClient.cs | 3 +- .../DD.Persistence.Client.csproj | 8 ++-- .../ChangeLogDto.cs | 0 .../Common/DatesRangeDto.cs | 0 .../Common/PaginationContainer.cs | 0 .../Configurations/AuthUser.cs | 0 .../Configurations/JwtParams.cs | 0 .../Configurations/JwtToken.cs | 0 .../Configurations/WitsInfo.cs | 0 .../DD.Persistence.Models.csproj | 43 +++++++++++++++++++ .../DataSourceSystemDto.cs | 0 .../DataWithWellDepthAndSectionDto.cs | 0 .../Enumerations/EventState.cs | 0 .../Enumerations/WitsType.cs | 0 .../ITimeSeriesAbstractDto.cs | 0 .../IWithSectionPart.cs | 0 .../MessagesStatisticDto.cs | 0 .../ParameterDto.cs | 0 .../Requests/PaginationRequest.cs | 0 .../Requests/SectionPartRequest.cs | 0 .../SetpointLogDto.cs | 0 .../SetpointValueDto.cs | 0 .../TechMessageDto.cs | 0 .../TimestampedValuesDto.cs | 0 .../ValuesIdentityDto.cs | 0 .../WitsDataDto.cs | 0 .../WitsValueDto.cs | 0 .../Repositories/TechMessagesRepository.cs | 1 + DD.Persistence.sln | 6 +++ DD.Persistence/DD.Persistence.csproj | 5 ++- .../Repositories/ITechMessagesRepository.cs | 1 + 32 files changed, 61 insertions(+), 9 deletions(-) rename {DD.Persistence/Models => DD.Persistence.Models}/ChangeLogDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/Common/DatesRangeDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/Common/PaginationContainer.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/Configurations/AuthUser.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/Configurations/JwtParams.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/Configurations/JwtToken.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/Configurations/WitsInfo.cs (100%) create mode 100644 DD.Persistence.Models/DD.Persistence.Models.csproj rename {DD.Persistence/Models => DD.Persistence.Models}/DataSourceSystemDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/DataWithWellDepthAndSectionDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/Enumerations/EventState.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/Enumerations/WitsType.cs (100%) rename DD.Persistence/ModelsAbstractions/ITimestampAbstractDto.cs => DD.Persistence.Models/ITimeSeriesAbstractDto.cs (100%) rename {DD.Persistence/ModelsAbstractions => DD.Persistence.Models}/IWithSectionPart.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/MessagesStatisticDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/ParameterDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/Requests/PaginationRequest.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/Requests/SectionPartRequest.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/SetpointLogDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/SetpointValueDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/TechMessageDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/TimestampedValuesDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/ValuesIdentityDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/WitsDataDto.cs (100%) rename {DD.Persistence/Models => DD.Persistence.Models}/WitsValueDto.cs (100%) diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTechMessagesClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTechMessagesClient.cs index 862d1c7..ef9496e 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTechMessagesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTechMessagesClient.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Models; +using DD.Persistence.Models; using DD.Persistence.Models.Requests; using Refit; using DD.Persistence.Models.Common; diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitWitsDataClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitWitsDataClient.cs index 9f719c6..233083a 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitWitsDataClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitWitsDataClient.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Models; +using DD.Persistence.Models; using Refit; using DD.Persistence.Models.Common; diff --git a/DD.Persistence.Client/DD.Persistence.Client.csproj b/DD.Persistence.Client/DD.Persistence.Client.csproj index 8acc827..12bdea4 100644 --- a/DD.Persistence.Client/DD.Persistence.Client.csproj +++ b/DD.Persistence.Client/DD.Persistence.Client.csproj @@ -9,13 +9,13 @@ True - Persistence.Client + DD.Persistence.Client 1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) 1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) - Persistence.Client + DD.Persistence.Client Digital Drilling @@ -33,7 +33,7 @@ snupkg - C:\Projects\Nuget + C:\Projects\Nuget\Persistence\Client Readme.md @@ -58,7 +58,7 @@ - + diff --git a/DD.Persistence/Models/ChangeLogDto.cs b/DD.Persistence.Models/ChangeLogDto.cs similarity index 100% rename from DD.Persistence/Models/ChangeLogDto.cs rename to DD.Persistence.Models/ChangeLogDto.cs diff --git a/DD.Persistence/Models/Common/DatesRangeDto.cs b/DD.Persistence.Models/Common/DatesRangeDto.cs similarity index 100% rename from DD.Persistence/Models/Common/DatesRangeDto.cs rename to DD.Persistence.Models/Common/DatesRangeDto.cs diff --git a/DD.Persistence/Models/Common/PaginationContainer.cs b/DD.Persistence.Models/Common/PaginationContainer.cs similarity index 100% rename from DD.Persistence/Models/Common/PaginationContainer.cs rename to DD.Persistence.Models/Common/PaginationContainer.cs diff --git a/DD.Persistence/Models/Configurations/AuthUser.cs b/DD.Persistence.Models/Configurations/AuthUser.cs similarity index 100% rename from DD.Persistence/Models/Configurations/AuthUser.cs rename to DD.Persistence.Models/Configurations/AuthUser.cs diff --git a/DD.Persistence/Models/Configurations/JwtParams.cs b/DD.Persistence.Models/Configurations/JwtParams.cs similarity index 100% rename from DD.Persistence/Models/Configurations/JwtParams.cs rename to DD.Persistence.Models/Configurations/JwtParams.cs diff --git a/DD.Persistence/Models/Configurations/JwtToken.cs b/DD.Persistence.Models/Configurations/JwtToken.cs similarity index 100% rename from DD.Persistence/Models/Configurations/JwtToken.cs rename to DD.Persistence.Models/Configurations/JwtToken.cs diff --git a/DD.Persistence/Models/Configurations/WitsInfo.cs b/DD.Persistence.Models/Configurations/WitsInfo.cs similarity index 100% rename from DD.Persistence/Models/Configurations/WitsInfo.cs rename to DD.Persistence.Models/Configurations/WitsInfo.cs diff --git a/DD.Persistence.Models/DD.Persistence.Models.csproj b/DD.Persistence.Models/DD.Persistence.Models.csproj new file mode 100644 index 0000000..dc4772e --- /dev/null +++ b/DD.Persistence.Models/DD.Persistence.Models.csproj @@ -0,0 +1,43 @@ + + + + net9.0 + enable + enable + true + + + True + + DD.Persistence.Models + + 1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) + + 1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) + + DD.Persistence.Models + + + Digital Drilling + + Digital Drilling + + Пакет для получения dtos для работы с Persistence сервисом + + + https://git.ddrilling.ru/on.nemtina/persistence.git + + git + + true + + snupkg + + C:\Projects\Nuget\Persistence\Models + + + + + + + diff --git a/DD.Persistence/Models/DataSourceSystemDto.cs b/DD.Persistence.Models/DataSourceSystemDto.cs similarity index 100% rename from DD.Persistence/Models/DataSourceSystemDto.cs rename to DD.Persistence.Models/DataSourceSystemDto.cs diff --git a/DD.Persistence/Models/DataWithWellDepthAndSectionDto.cs b/DD.Persistence.Models/DataWithWellDepthAndSectionDto.cs similarity index 100% rename from DD.Persistence/Models/DataWithWellDepthAndSectionDto.cs rename to DD.Persistence.Models/DataWithWellDepthAndSectionDto.cs diff --git a/DD.Persistence/Models/Enumerations/EventState.cs b/DD.Persistence.Models/Enumerations/EventState.cs similarity index 100% rename from DD.Persistence/Models/Enumerations/EventState.cs rename to DD.Persistence.Models/Enumerations/EventState.cs diff --git a/DD.Persistence/Models/Enumerations/WitsType.cs b/DD.Persistence.Models/Enumerations/WitsType.cs similarity index 100% rename from DD.Persistence/Models/Enumerations/WitsType.cs rename to DD.Persistence.Models/Enumerations/WitsType.cs diff --git a/DD.Persistence/ModelsAbstractions/ITimestampAbstractDto.cs b/DD.Persistence.Models/ITimeSeriesAbstractDto.cs similarity index 100% rename from DD.Persistence/ModelsAbstractions/ITimestampAbstractDto.cs rename to DD.Persistence.Models/ITimeSeriesAbstractDto.cs diff --git a/DD.Persistence/ModelsAbstractions/IWithSectionPart.cs b/DD.Persistence.Models/IWithSectionPart.cs similarity index 100% rename from DD.Persistence/ModelsAbstractions/IWithSectionPart.cs rename to DD.Persistence.Models/IWithSectionPart.cs diff --git a/DD.Persistence/Models/MessagesStatisticDto.cs b/DD.Persistence.Models/MessagesStatisticDto.cs similarity index 100% rename from DD.Persistence/Models/MessagesStatisticDto.cs rename to DD.Persistence.Models/MessagesStatisticDto.cs diff --git a/DD.Persistence/Models/ParameterDto.cs b/DD.Persistence.Models/ParameterDto.cs similarity index 100% rename from DD.Persistence/Models/ParameterDto.cs rename to DD.Persistence.Models/ParameterDto.cs diff --git a/DD.Persistence/Models/Requests/PaginationRequest.cs b/DD.Persistence.Models/Requests/PaginationRequest.cs similarity index 100% rename from DD.Persistence/Models/Requests/PaginationRequest.cs rename to DD.Persistence.Models/Requests/PaginationRequest.cs diff --git a/DD.Persistence/Models/Requests/SectionPartRequest.cs b/DD.Persistence.Models/Requests/SectionPartRequest.cs similarity index 100% rename from DD.Persistence/Models/Requests/SectionPartRequest.cs rename to DD.Persistence.Models/Requests/SectionPartRequest.cs diff --git a/DD.Persistence/Models/SetpointLogDto.cs b/DD.Persistence.Models/SetpointLogDto.cs similarity index 100% rename from DD.Persistence/Models/SetpointLogDto.cs rename to DD.Persistence.Models/SetpointLogDto.cs diff --git a/DD.Persistence/Models/SetpointValueDto.cs b/DD.Persistence.Models/SetpointValueDto.cs similarity index 100% rename from DD.Persistence/Models/SetpointValueDto.cs rename to DD.Persistence.Models/SetpointValueDto.cs diff --git a/DD.Persistence/Models/TechMessageDto.cs b/DD.Persistence.Models/TechMessageDto.cs similarity index 100% rename from DD.Persistence/Models/TechMessageDto.cs rename to DD.Persistence.Models/TechMessageDto.cs diff --git a/DD.Persistence/Models/TimestampedValuesDto.cs b/DD.Persistence.Models/TimestampedValuesDto.cs similarity index 100% rename from DD.Persistence/Models/TimestampedValuesDto.cs rename to DD.Persistence.Models/TimestampedValuesDto.cs diff --git a/DD.Persistence/Models/ValuesIdentityDto.cs b/DD.Persistence.Models/ValuesIdentityDto.cs similarity index 100% rename from DD.Persistence/Models/ValuesIdentityDto.cs rename to DD.Persistence.Models/ValuesIdentityDto.cs diff --git a/DD.Persistence/Models/WitsDataDto.cs b/DD.Persistence.Models/WitsDataDto.cs similarity index 100% rename from DD.Persistence/Models/WitsDataDto.cs rename to DD.Persistence.Models/WitsDataDto.cs diff --git a/DD.Persistence/Models/WitsValueDto.cs b/DD.Persistence.Models/WitsValueDto.cs similarity index 100% rename from DD.Persistence/Models/WitsValueDto.cs rename to DD.Persistence.Models/WitsValueDto.cs diff --git a/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs b/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs index 274a940..6d4f3b1 100644 --- a/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -89,6 +89,7 @@ namespace DD.Persistence.Repository.Repositories await CreateSystemIfNotExist(systemId, token); entity.SystemId = systemId; + entity.Timestamp = dto.Timestamp.ToUniversalTime(); entities.Add(entity); } diff --git a/DD.Persistence.sln b/DD.Persistence.sln index 22c6b2a..5fb5aee 100644 --- a/DD.Persistence.sln +++ b/DD.Persistence.sln @@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DD.Persistence.Client", "DD EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DD.Persistence.App", "DD.Persistence.App\DD.Persistence.App.csproj", "{063238BF-E982-43FA-9DDB-7D7D279086D8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DD.Persistence.Models", "DD.Persistence.Models\DD.Persistence.Models.csproj", "{698B4571-BB7A-4A42-8B0B-6C7F2F5360FB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -57,6 +59,10 @@ Global {063238BF-E982-43FA-9DDB-7D7D279086D8}.Debug|Any CPU.Build.0 = Debug|Any CPU {063238BF-E982-43FA-9DDB-7D7D279086D8}.Release|Any CPU.ActiveCfg = Release|Any CPU {063238BF-E982-43FA-9DDB-7D7D279086D8}.Release|Any CPU.Build.0 = Release|Any CPU + {698B4571-BB7A-4A42-8B0B-6C7F2F5360FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {698B4571-BB7A-4A42-8B0B-6C7F2F5360FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {698B4571-BB7A-4A42-8B0B-6C7F2F5360FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {698B4571-BB7A-4A42-8B0B-6C7F2F5360FB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/DD.Persistence/DD.Persistence.csproj b/DD.Persistence/DD.Persistence.csproj index f1c1782..781cf02 100644 --- a/DD.Persistence/DD.Persistence.csproj +++ b/DD.Persistence/DD.Persistence.csproj @@ -18,7 +18,10 @@ - + + + + diff --git a/DD.Persistence/Repositories/ITechMessagesRepository.cs b/DD.Persistence/Repositories/ITechMessagesRepository.cs index 6244485..cd3c826 100644 --- a/DD.Persistence/Repositories/ITechMessagesRepository.cs +++ b/DD.Persistence/Repositories/ITechMessagesRepository.cs @@ -20,6 +20,7 @@ namespace DD.Persistence.Repositories /// /// Добавление новых сообщений /// + /// /// /// /// -- 2.45.2 From 21664d1e16b02cf0dfff2132aa70771224736e56 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Mon, 20 Jan 2025 09:05:01 +0500 Subject: [PATCH 08/16] =?UTF-8?q?=20=D0=A4=D0=B8=D0=BA=D1=81=20Setpoint=20?= =?UTF-8?q?Timestamp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/SetpointControllerTest.cs | 31 +++++++++---------- DD.Persistence.Models/SetpointLogDto.cs | 2 +- .../Repositories/SetpointRepository.cs | 2 +- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs index 27432f3..5b36d6a 100644 --- a/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs @@ -1,23 +1,17 @@ -using Microsoft.Extensions.DependencyInjection; using DD.Persistence.Client; -using DD.Persistence.Client.Clients.Interfaces; -using DD.Persistence.Database.Model; -using System.Net; -using Xunit; -using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Client.Clients; +using DD.Persistence.Client.Clients.Interfaces; +using DD.Persistence.Client.Clients.Interfaces.Refit; +using DD.Persistence.Database.Model; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Xunit; namespace DD.Persistence.IntegrationTests.Controllers { public class SetpointControllerTest : BaseIntegrationTest { private readonly ISetpointClient setpointClient; - private class TestObject - { - public string? Value1 { get; set; } - public int? Value2 { get; set; } - } public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory) { var refitClientFactory = scope.ServiceProvider @@ -153,7 +147,7 @@ namespace DD.Persistence.IntegrationTests.Controllers await Add(); - var dateBegin = DateTimeOffset.MinValue; + var dateBegin = DateTimeOffset.UtcNow.AddDays(-1); var take = 1; var part = await setpointClient.GetPart(dateBegin, take, CancellationToken.None); @@ -164,14 +158,17 @@ namespace DD.Persistence.IntegrationTests.Controllers Assert.NotNull(response); var expectedValue = part! - .FirstOrDefault()!.Created - .ToString("dd.MM.yyyy-HH:mm:ss"); + .FirstOrDefault()!.Timestamp + .ToUniversalTime() + .ToString(); var actualValueFrom = response.From - .ToString("dd.MM.yyyy-HH:mm:ss"); + .ToUniversalTime() + .ToString(); Assert.Equal(expectedValue, actualValueFrom); var actualValueTo = response.To - .ToString("dd.MM.yyyy-HH:mm:ss"); + .ToUniversalTime() + .ToString(); Assert.Equal(expectedValue, actualValueTo); } @@ -216,7 +213,7 @@ namespace DD.Persistence.IntegrationTests.Controllers { //arrange var setpointKey = Guid.NewGuid(); - var setpointValue = new TestObject() + var setpointValue = new { Value1 = "1", Value2 = 2 diff --git a/DD.Persistence.Models/SetpointLogDto.cs b/DD.Persistence.Models/SetpointLogDto.cs index 9a46a99..78cc66c 100644 --- a/DD.Persistence.Models/SetpointLogDto.cs +++ b/DD.Persistence.Models/SetpointLogDto.cs @@ -8,7 +8,7 @@ public class SetpointLogDto : SetpointValueDto /// /// Дата сохранения уставки /// - public DateTimeOffset Created { get; set; } + public DateTimeOffset Timestamp { get; set; } /// /// Ключ пользователя diff --git a/DD.Persistence.Repository/Repositories/SetpointRepository.cs b/DD.Persistence.Repository/Repositories/SetpointRepository.cs index 8068b79..8fb703c 100644 --- a/DD.Persistence.Repository/Repositories/SetpointRepository.cs +++ b/DD.Persistence.Repository/Repositories/SetpointRepository.cs @@ -96,7 +96,7 @@ namespace DD.Persistence.Repository.Repositories Key = setpointKey, Value = newValue, IdUser = idUser, - Timestamp = DateTimeOffset.UtcNow + Timestamp = DateTimeOffset.UtcNow.ToUniversalTime() }; await db.Set().AddAsync(entity, token); -- 2.45.2 From 89fe8a1807e38cca2a38bb06020abb11221cdf26 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Mon, 20 Jan 2025 09:09:07 +0500 Subject: [PATCH 09/16] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BD=D0=B0?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DD.Persistence.App/appsettings.Tests.json | 2 +- DD.Persistence.App/appsettings.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DD.Persistence.App/appsettings.Tests.json b/DD.Persistence.App/appsettings.Tests.json index 9934757..72c43d3 100644 --- a/DD.Persistence.App/appsettings.Tests.json +++ b/DD.Persistence.App/appsettings.Tests.json @@ -1,6 +1,6 @@ { "DbConnection": { - "Host": "localhost", + "Host": "postgres", "Port": 5432, "Database": "persistence", "Username": "postgres", diff --git a/DD.Persistence.App/appsettings.json b/DD.Persistence.App/appsettings.json index 0af1529..7ad8c67 100644 --- a/DD.Persistence.App/appsettings.json +++ b/DD.Persistence.App/appsettings.json @@ -6,7 +6,7 @@ } }, "ConnectionStrings": { - "DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=q;Persist Security Info=True" + "DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True" }, "AllowedHosts": "*", "NeedUseKeyCloak": false, -- 2.45.2 From 156328f2b4c854d19f31d91fdd490da751621cec Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Mon, 20 Jan 2025 09:16:36 +0500 Subject: [PATCH 10/16] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BE=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DD.Persistence.API/Controllers/TimestampedValuesController.cs | 1 - .../Clients/Interfaces/ITimestampedValuesClient.cs | 2 +- DD.Persistence.Client/Readme.md | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/DD.Persistence.API/Controllers/TimestampedValuesController.cs b/DD.Persistence.API/Controllers/TimestampedValuesController.cs index c4d498b..66206ad 100644 --- a/DD.Persistence.API/Controllers/TimestampedValuesController.cs +++ b/DD.Persistence.API/Controllers/TimestampedValuesController.cs @@ -8,7 +8,6 @@ namespace DD.Persistence.API.Controllers; /// /// Хранение наборов данных с отметкой времени. -/// Не оптимизировано под большие данные. /// [ApiController] //[Authorize] diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs index 23f1ddf..c193fc5 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs @@ -4,7 +4,7 @@ using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients.Interfaces; /// -/// Клиент для работы с репозиторием для хранения разных наборов данных рядов. +/// Клиент для работы с наборами данных, имеющими отметку времени. /// discriminatorId - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest. /// discriminatorId формируют клиенты и только им известно что они обозначают. /// diff --git a/DD.Persistence.Client/Readme.md b/DD.Persistence.Client/Readme.md index 1e09e2f..6bdae87 100644 --- a/DD.Persistence.Client/Readme.md +++ b/DD.Persistence.Client/Readme.md @@ -11,8 +11,7 @@ Persistence сервисом посредством обращения к кон ## Список предоставляемых клиентов - `ISetpointClient` - Клиент для работы с уставками - `ITechMessagesClient` - Клиент для работы с технологическими сообщениями -- `ITimeSeriesClient` - Клиент для работы с временными данными -- `ITimestampedSetClient` - Клиент для работы с данными с отметкой времени +- `ITimestampedValuesClient` - Клиент для работы с наборами данных, имеющими отметку времени - `IChangeLogClient` - Клиент для работы с записями ChangeLog - `IWitsDataClient` - Клиент для работы с параметрами Wits - `IDataSourceSystemClient` - Клиент для работы с системами -- 2.45.2 From 16a14e7e193e7a72a480d6ea2355fb6cac4a3ac6 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Mon, 20 Jan 2025 17:11:44 +0500 Subject: [PATCH 11/16] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B7=D1=83=D0=BB=D1=8C=D1=82=D0=B0?= =?UTF-8?q?=D1=82=D0=B0=D0=BC=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/DataSourceSystemController.cs | 4 +- .../TimestampedValuesController.cs | 19 +- DD.Persistence.API/DependencyInjection.cs | 1 + .../{ValuesIdentity.cs => DataScheme.cs} | 6 +- .../Entity/TimestampedValues.cs | 2 +- .../PersistenceDbContext.cs | 6 +- .../TimestampedValuesControllerTest.cs | 40 +++- DD.Persistence.Models/DataSchemeDto.cs | 17 ++ DD.Persistence.Models/ValuesIdentityDto.cs | 17 -- .../DependencyInjection.cs | 6 +- DD.Persistence.Repository/QueryBuilders.cs | 1 + .../Repositories/DataSchemeRepository.cs | 34 +++ .../DataSourceSystemRepository.cs | 33 +++ .../Repositories/RelatedDataRepository.cs | 33 --- .../Repositories/TechMessagesRepository.cs | 5 +- .../TimestampedValuesRepository.cs | 149 ++++--------- .../DataSchemeCachedRepository.cs | 30 +++ ...cs => DataSourceSystemCachedRepository.cs} | 16 +- .../TimestampedValuesCachedRepository.cs | 2 +- .../{ => Extensions}/EFExtensions.cs | 4 +- .../Extensions/IEnumerableExtensions.cs | 2 +- .../Repositories/IDataSchemeRepository.cs | 25 +++ .../IDataSourceSystemRepository.cs | 23 ++ .../Repositories/IRelatedDataRepository.cs | 23 -- .../ITimestampedValuesRepository.cs | 10 +- .../ISyncRepository.cs | 4 +- .../ITimeSeriesBaseRepository.cs | 4 +- .../Interfaces/ITimestampedValuesService.cs | 85 ++++++++ .../Services/TimestampedValuesService.cs | 201 ++++++++++++++++++ 29 files changed, 580 insertions(+), 222 deletions(-) rename DD.Persistence.Database/Entity/{ValuesIdentity.cs => DataScheme.cs} (60%) create mode 100644 DD.Persistence.Models/DataSchemeDto.cs delete mode 100644 DD.Persistence.Models/ValuesIdentityDto.cs create mode 100644 DD.Persistence.Repository/Repositories/DataSchemeRepository.cs create mode 100644 DD.Persistence.Repository/Repositories/DataSourceSystemRepository.cs delete mode 100644 DD.Persistence.Repository/Repositories/RelatedDataRepository.cs create mode 100644 DD.Persistence.Repository/RepositoriesCached/DataSchemeCachedRepository.cs rename DD.Persistence.Repository/RepositoriesCached/{RelatedDataCachedRepository.cs => DataSourceSystemCachedRepository.cs} (56%) rename DD.Persistence/{ => Extensions}/EFExtensions.cs (98%) rename {DD.Persistence.Repository => DD.Persistence}/Extensions/IEnumerableExtensions.cs (93%) create mode 100644 DD.Persistence/Repositories/IDataSchemeRepository.cs create mode 100644 DD.Persistence/Repositories/IDataSourceSystemRepository.cs delete mode 100644 DD.Persistence/Repositories/IRelatedDataRepository.cs create mode 100644 DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs create mode 100644 DD.Persistence/Services/TimestampedValuesService.cs diff --git a/DD.Persistence.API/Controllers/DataSourceSystemController.cs b/DD.Persistence.API/Controllers/DataSourceSystemController.cs index 5e551a9..896af1f 100644 --- a/DD.Persistence.API/Controllers/DataSourceSystemController.cs +++ b/DD.Persistence.API/Controllers/DataSourceSystemController.cs @@ -14,9 +14,9 @@ namespace DD.Persistence.API.Controllers; [Route("api/[controller]")] public class DataSourceSystemController : ControllerBase { - private readonly IRelatedDataRepository dataSourceSystemRepository; + private readonly IDataSourceSystemRepository dataSourceSystemRepository; - public DataSourceSystemController(IRelatedDataRepository dataSourceSystemRepository) + public DataSourceSystemController(IDataSourceSystemRepository dataSourceSystemRepository) { this.dataSourceSystemRepository = dataSourceSystemRepository; } diff --git a/DD.Persistence.API/Controllers/TimestampedValuesController.cs b/DD.Persistence.API/Controllers/TimestampedValuesController.cs index 66206ad..759d88a 100644 --- a/DD.Persistence.API/Controllers/TimestampedValuesController.cs +++ b/DD.Persistence.API/Controllers/TimestampedValuesController.cs @@ -1,6 +1,7 @@ using DD.Persistence.Models; using DD.Persistence.Models.Common; -using DD.Persistence.Repositories; +using DD.Persistence.Services.Interfaces; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Net; @@ -10,13 +11,13 @@ namespace DD.Persistence.API.Controllers; /// Хранение наборов данных с отметкой времени. /// [ApiController] -//[Authorize] +[Authorize] [Route("api/[controller]/{discriminatorId}")] public class TimestampedValuesController : ControllerBase { - private readonly ITimestampedValuesRepository timestampedValuesRepository; + private readonly ITimestampedValuesService timestampedValuesRepository; - public TimestampedValuesController(ITimestampedValuesRepository repository) + public TimestampedValuesController(ITimestampedValuesService repository) { this.timestampedValuesRepository = repository; } @@ -47,6 +48,8 @@ public class TimestampedValuesController : ControllerBase /// /// [HttpGet] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] public async Task>> Get([FromRoute] Guid discriminatorId, DateTimeOffset? timestampBegin, [FromQuery] string[]? columnNames, int skip, int take, CancellationToken token) { var result = await timestampedValuesRepository.Get(discriminatorId, timestampBegin, columnNames, skip, take, token); @@ -61,6 +64,8 @@ public class TimestampedValuesController : ControllerBase /// Фильтр позднее даты /// [HttpGet("gtdate")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] public async Task>> GetGtDate([FromRoute] Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token) { var result = await timestampedValuesRepository.GetGtDate(discriminatorId, timestampBegin, token); @@ -75,6 +80,8 @@ public class TimestampedValuesController : ControllerBase /// /// [HttpGet("first")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] public async Task>> GetFirst([FromRoute] Guid discriminatorId, int take, CancellationToken token) { var result = await timestampedValuesRepository.GetFirst(discriminatorId, take, token); @@ -89,6 +96,8 @@ public class TimestampedValuesController : ControllerBase /// /// [HttpGet("last")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] public async Task>> GetLast([FromRoute] Guid discriminatorId, int take, CancellationToken token) { var result = await timestampedValuesRepository.GetLast(discriminatorId, take, token); @@ -105,6 +114,8 @@ public class TimestampedValuesController : ControllerBase /// /// [HttpGet("resampled")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] public async Task>> GetResampledData([FromRoute] Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) { var result = await timestampedValuesRepository.GetResampledData(discriminatorId, timestampBegin, intervalSec, approxPointsCount, token); diff --git a/DD.Persistence.API/DependencyInjection.cs b/DD.Persistence.API/DependencyInjection.cs index 6b0754c..b543841 100644 --- a/DD.Persistence.API/DependencyInjection.cs +++ b/DD.Persistence.API/DependencyInjection.cs @@ -53,6 +53,7 @@ public static class DependencyInjection public static void AddServices(this IServiceCollection services) { services.AddTransient(); + services.AddTransient(); } #region Authentication diff --git a/DD.Persistence.Database/Entity/ValuesIdentity.cs b/DD.Persistence.Database/Entity/DataScheme.cs similarity index 60% rename from DD.Persistence.Database/Entity/ValuesIdentity.cs rename to DD.Persistence.Database/Entity/DataScheme.cs index dc7f607..7936d0c 100644 --- a/DD.Persistence.Database/Entity/ValuesIdentity.cs +++ b/DD.Persistence.Database/Entity/DataScheme.cs @@ -4,11 +4,11 @@ using System.ComponentModel.DataAnnotations.Schema; namespace DD.Persistence.Database.Entity; -public class ValuesIdentity +public class DataScheme { [Key, Comment("Дискриминатор системы"),] public Guid DiscriminatorId { get; set; } - [Comment("Идентификаторы"), Column(TypeName = "jsonb")] - public string[] Identity { get; set; } = []; + [Comment("Наименования полей в порядке индексации"), Column(TypeName = "jsonb")] + public string[] PropNames { get; set; } = []; } diff --git a/DD.Persistence.Database/Entity/TimestampedValues.cs b/DD.Persistence.Database/Entity/TimestampedValues.cs index 05c7a57..28c8946 100644 --- a/DD.Persistence.Database/Entity/TimestampedValues.cs +++ b/DD.Persistence.Database/Entity/TimestampedValues.cs @@ -18,5 +18,5 @@ public class TimestampedValues : ITimestampedItem public required object[] Values { get; set; } [Required, ForeignKey(nameof(DiscriminatorId)), Comment("Идентификаторы")] - public virtual ValuesIdentity? ValuesIdentity { get; set; } + public virtual DataScheme? DataScheme { get; set; } } diff --git a/DD.Persistence.Database/PersistenceDbContext.cs b/DD.Persistence.Database/PersistenceDbContext.cs index 18a01cc..5a9a13c 100644 --- a/DD.Persistence.Database/PersistenceDbContext.cs +++ b/DD.Persistence.Database/PersistenceDbContext.cs @@ -11,7 +11,7 @@ public class PersistenceDbContext : DbContext { public DbSet Setpoint => Set(); - public DbSet ValuesIdentities => Set(); + public DbSet DataSchemes => Set(); public DbSet TimestampedValues => Set(); @@ -31,8 +31,8 @@ public class PersistenceDbContext : DbContext protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity() - .Property(e => e.Identity) + modelBuilder.Entity() + .Property(e => e.PropNames) .HasJsonConversion(); modelBuilder.Entity() diff --git a/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs index b166631..71a49d5 100644 --- a/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs @@ -13,9 +13,9 @@ using Xunit; namespace DD.Persistence.IntegrationTests.Controllers; public class TimestampedValuesControllerTest : BaseIntegrationTest { - private static readonly string SystemCacheKey = $"{typeof(ValuesIdentity).FullName}CacheKey"; private readonly ITimestampedValuesClient timestampedValuesClient; private readonly IMemoryCache memoryCache; + private IEnumerable discriminatorIds = []; public TimestampedValuesControllerTest(WebAppFactoryFixture factory) : base(factory) { @@ -32,6 +32,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest public async Task AddRange_returns_success() { var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); await AddRange(discriminatorId); } @@ -43,6 +44,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest Cleanup(); var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); //act var response = await timestampedValuesClient.Get(discriminatorId, null, null, 0, 1, CancellationToken.None); @@ -59,6 +61,8 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest Cleanup(); var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var timestampBegin = DateTimeOffset.UtcNow.AddDays(-1); var columnNames = new List() { "A", "C" }; var skip = 5; @@ -94,6 +98,8 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest //arrange Cleanup(); var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var timestampBegin = DateTimeOffset.UtcNow.AddDays(-1); //act @@ -109,6 +115,8 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest //arrange Cleanup(); var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var dtos = await AddRange(discriminatorId); var timestampBegin = DateTimeOffset.UtcNow.AddSeconds(-5); @@ -130,6 +138,8 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest //arrange Cleanup(); var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var take = 1; //act @@ -145,6 +155,8 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest //arrange Cleanup(); var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var dtos = await AddRange(discriminatorId); var take = 1; @@ -172,7 +184,10 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest { //arrange Cleanup(); + var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var take = 1; //act @@ -187,7 +202,10 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest { //arrange Cleanup(); + var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var dtos = await AddRange(discriminatorId); var take = 1; @@ -215,7 +233,10 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest { //arrange Cleanup(); + var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var timestampBegin = DateTimeOffset.UtcNow; //act @@ -230,7 +251,10 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest { //arrange Cleanup(); + var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var count = 2048; var timestampBegin = DateTimeOffset.UtcNow; var dtos = await AddRange(discriminatorId, count); @@ -253,7 +277,9 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest { //arrange Cleanup(); + var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); //act var response = await timestampedValuesClient.Count(discriminatorId, CancellationToken.None); @@ -267,7 +293,10 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest { //arrange Cleanup(); + var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var dtos = await AddRange(discriminatorId); //act @@ -283,7 +312,9 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest { //arrange Cleanup(); + var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); //act var response = await timestampedValuesClient.GetDatesRange(discriminatorId, CancellationToken.None); @@ -297,7 +328,10 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest { //arrange Cleanup(); + var discriminatorId = Guid.NewGuid(); + discriminatorIds.Append(discriminatorId); + var dtos = await AddRange(discriminatorId); //act @@ -364,8 +398,8 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest private void Cleanup() { - memoryCache.Remove(SystemCacheKey); + discriminatorIds = []; dbContext.CleanupDbSet(); - dbContext.CleanupDbSet(); + dbContext.CleanupDbSet(); } } diff --git a/DD.Persistence.Models/DataSchemeDto.cs b/DD.Persistence.Models/DataSchemeDto.cs new file mode 100644 index 0000000..d1efdd5 --- /dev/null +++ b/DD.Persistence.Models/DataSchemeDto.cs @@ -0,0 +1,17 @@ +namespace DD.Persistence.Models; + +/// +/// Схема для набора данных +/// +public class DataSchemeDto +{ + /// + /// Дискриминатор + /// + public Guid DiscriminatorId { get; set; } + + /// + /// Наименования полей + /// + public string[] PropNames { get; set; } = []; +} diff --git a/DD.Persistence.Models/ValuesIdentityDto.cs b/DD.Persistence.Models/ValuesIdentityDto.cs deleted file mode 100644 index 36d91fc..0000000 --- a/DD.Persistence.Models/ValuesIdentityDto.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace DD.Persistence.Models; - -/// -/// Набор идентификаторов для набора данных -/// -public class ValuesIdentityDto -{ - /// - /// Дискриминатор системы - /// - public Guid DiscriminatorId { get; set; } - - /// - /// Идентификаторы - /// - public string[] Identity { get; set; } = []; -} diff --git a/DD.Persistence.Repository/DependencyInjection.cs b/DD.Persistence.Repository/DependencyInjection.cs index 498849a..d0a5d37 100644 --- a/DD.Persistence.Repository/DependencyInjection.cs +++ b/DD.Persistence.Repository/DependencyInjection.cs @@ -41,10 +41,8 @@ public static class DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient, - RelatedDataCachedRepository>(); - services.AddTransient, - RelatedDataCachedRepository>(); + services.AddTransient(); + services.AddTransient(); return services; } diff --git a/DD.Persistence.Repository/QueryBuilders.cs b/DD.Persistence.Repository/QueryBuilders.cs index 5fb3eb5..53e72a4 100644 --- a/DD.Persistence.Repository/QueryBuilders.cs +++ b/DD.Persistence.Repository/QueryBuilders.cs @@ -3,6 +3,7 @@ using DD.Persistence.Models.Requests; using DD.Persistence.Models.Common; using DD.Persistence.ModelsAbstractions; using DD.Persistence.Database.EntityAbstractions; +using DD.Persistence.Extensions; namespace DD.Persistence.Repository; diff --git a/DD.Persistence.Repository/Repositories/DataSchemeRepository.cs b/DD.Persistence.Repository/Repositories/DataSchemeRepository.cs new file mode 100644 index 0000000..3fe99d2 --- /dev/null +++ b/DD.Persistence.Repository/Repositories/DataSchemeRepository.cs @@ -0,0 +1,34 @@ +using DD.Persistence.Database.Entity; +using DD.Persistence.Models; +using DD.Persistence.Repositories; +using Mapster; +using Microsoft.EntityFrameworkCore; + +namespace DD.Persistence.Repository.Repositories; +public class DataSchemeRepository : IDataSchemeRepository +{ + protected DbContext db; + public DataSchemeRepository(DbContext db) + { + this.db = db; + } + protected virtual IQueryable GetQueryReadOnly() => db.Set(); + + public virtual async Task Add(DataSchemeDto dataSourceSystemDto, CancellationToken token) + { + var entity = dataSourceSystemDto.Adapt(); + + await db.Set().AddAsync(entity, token); + await db.SaveChangesAsync(token); + } + + public virtual async Task GetByDiscriminator(Guid discriminatorId, CancellationToken token) + { + var query = GetQueryReadOnly() + .Where(e => e.DiscriminatorId == discriminatorId); + var entity = await query.ToArrayAsync(); + var dto = entity.Select(e => e.Adapt()).FirstOrDefault(); + + return dto; + } +} diff --git a/DD.Persistence.Repository/Repositories/DataSourceSystemRepository.cs b/DD.Persistence.Repository/Repositories/DataSourceSystemRepository.cs new file mode 100644 index 0000000..d8b6c0a --- /dev/null +++ b/DD.Persistence.Repository/Repositories/DataSourceSystemRepository.cs @@ -0,0 +1,33 @@ +using DD.Persistence.Database.Entity; +using DD.Persistence.Models; +using DD.Persistence.Repositories; +using Mapster; +using Microsoft.EntityFrameworkCore; + +namespace DD.Persistence.Repository.Repositories; +public class DataSourceSystemRepository : IDataSourceSystemRepository +{ + protected DbContext db; + public DataSourceSystemRepository(DbContext db) + { + this.db = db; + } + protected virtual IQueryable GetQueryReadOnly() => db.Set(); + + public virtual async Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token) + { + var entity = dataSourceSystemDto.Adapt(); + + await db.Set().AddAsync(entity, token); + await db.SaveChangesAsync(token); + } + + public virtual async Task> Get(CancellationToken token) + { + var query = GetQueryReadOnly(); + var entities = await query.ToArrayAsync(token); + var dtos = entities.Select(e => e.Adapt()); + + return dtos; + } +} diff --git a/DD.Persistence.Repository/Repositories/RelatedDataRepository.cs b/DD.Persistence.Repository/Repositories/RelatedDataRepository.cs deleted file mode 100644 index 7efc329..0000000 --- a/DD.Persistence.Repository/Repositories/RelatedDataRepository.cs +++ /dev/null @@ -1,33 +0,0 @@ -using DD.Persistence.Repositories; -using Mapster; -using Microsoft.EntityFrameworkCore; - -namespace DD.Persistence.Repository.Repositories; -public class RelatedDataRepository : IRelatedDataRepository - where TDto : class, new() - where TEntity : class, new() -{ - protected DbContext db; - public RelatedDataRepository(DbContext db) - { - this.db = db; - } - protected virtual IQueryable GetQueryReadOnly() => db.Set(); - - public virtual async Task Add(TDto dataSourceSystemDto, CancellationToken token) - { - var entity = dataSourceSystemDto.Adapt(); - - await db.Set().AddAsync(entity, token); - await db.SaveChangesAsync(token); - } - - public virtual async Task> Get(CancellationToken token) - { - var query = GetQueryReadOnly(); - var entities = await query.ToArrayAsync(token); - var dtos = entities.Select(e => e.Adapt()); - - return dtos; - } -} diff --git a/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs b/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs index 6d4f3b1..1a84f25 100644 --- a/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -1,4 +1,5 @@ using DD.Persistence.Database.Entity; +using DD.Persistence.Extensions; using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.Models.Requests; @@ -10,10 +11,10 @@ namespace DD.Persistence.Repository.Repositories { public class TechMessagesRepository : ITechMessagesRepository { - private readonly IRelatedDataRepository sourceSystemRepository; + private readonly IDataSourceSystemRepository sourceSystemRepository; private DbContext db; - public TechMessagesRepository(DbContext db, IRelatedDataRepository sourceSystemRepository) + public TechMessagesRepository(DbContext db, IDataSourceSystemRepository sourceSystemRepository) { this.db = db; this.sourceSystemRepository = sourceSystemRepository; diff --git a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs index 9e1597c..b2f8203 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs @@ -2,32 +2,25 @@ using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.Repositories; -using DD.Persistence.Repository.Extensions; using Microsoft.EntityFrameworkCore; namespace DD.Persistence.Repository.Repositories; public class TimestampedValuesRepository : ITimestampedValuesRepository { private readonly DbContext db; - private readonly IRelatedDataRepository relatedDataRepository; - public TimestampedValuesRepository(DbContext db, IRelatedDataRepository relatedDataRepository) + public TimestampedValuesRepository(DbContext db) { this.db = db; - this.relatedDataRepository = relatedDataRepository; } - protected virtual IQueryable GetQueryReadOnly() => this.db.Set() - .Include(e => e.ValuesIdentity); + protected virtual IQueryable GetQueryReadOnly() => this.db.Set(); public async virtual Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) { var timestampedValuesEntities = new List(); foreach (var dto in dtos) { - var keys = dto.Values.Keys.ToArray(); - await CreateValuesIdentityIfNotExist(discriminatorId, keys, token); - var timestampedValuesEntity = new TimestampedValues() { DiscriminatorId = discriminatorId, @@ -44,7 +37,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository return result; } - public async virtual Task> Get(Guid discriminatorId, DateTimeOffset? timestampBegin, IEnumerable? columnNames, int skip, int take, CancellationToken token) + public async virtual Task>> Get(Guid discriminatorId, DateTimeOffset? timestampBegin, IEnumerable? columnNames, int skip, int take, CancellationToken token) { var query = GetQueryReadOnly() .Where(entity => entity.DiscriminatorId == discriminatorId); @@ -59,68 +52,79 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository .OrderBy(item => item.Timestamp) .Skip(skip) .Take(take); - var data = await Materialize(discriminatorId, query, token); + var entities = await query.ToArrayAsync(token); - // Фильтрация по запрашиваемым полям - if (!columnNames.IsNullOrEmpty()) - { - data = ReduceSetColumnsByNames(data, columnNames!); - } + var result = entities.Select(e => Tuple.Create( + e.Timestamp, + e.Values + )); - return data; + return result; } - public async virtual Task> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token) + public async virtual Task>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token) { var query = GetQueryReadOnly() .OrderBy(e => e.Timestamp) .Take(takeCount); + var entities = await query.ToArrayAsync(token); - var dtos = await Materialize(discriminatorId, query, token); + var result = entities.Select(e => Tuple.Create( + e.Timestamp, + e.Values + )); - return dtos; + return result; } - public async virtual Task> GetLast(Guid discriminatorId, int takeCount, CancellationToken token) + public async virtual Task>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token) { var query = GetQueryReadOnly() .OrderByDescending(e => e.Timestamp) .Take(takeCount); + var entities = await query.ToArrayAsync(token); - var dtos = await Materialize(discriminatorId, query, token); + var result = entities.Select(e => Tuple.Create( + e.Timestamp, + e.Values + )); - return dtos; + return result; } - public async virtual Task> GetResampledData( + public async virtual Task>> GetResampledData( Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) { - var dtos = await GetGtDate(discriminatorId, dateBegin, token); + var result = await GetGtDate(discriminatorId, dateBegin, token); var dateEnd = dateBegin.AddSeconds(intervalSec); - dtos = dtos - .Where(i => i.Timestamp <= dateEnd); + result = result + .Where(i => i.Item1 <= dateEnd); - var ratio = dtos.Count() / approxPointsCount; + var ratio = result.Count() / approxPointsCount; if (ratio > 1) - dtos = dtos + result = result .Where((_, index) => index % ratio == 0); - return dtos; + return result; } - public async virtual Task> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token) + public async virtual Task>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token) { var query = GetQueryReadOnly() .Where(e => e.Timestamp > timestampBegin); + var entities = await query.ToArrayAsync(token); - var dtos = await Materialize(discriminatorId, query, token); + var result = entities.Select(e => Tuple.Create( + e.Timestamp, + e.Values + )); - return dtos; + return result; } public async virtual Task GetDatesRange(Guid discriminatorId, CancellationToken token) @@ -156,37 +160,12 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository return query.CountAsync(token); } - private async Task> Materialize(Guid discriminatorId, IQueryable query, CancellationToken token) - { - var valuesIdentities = await relatedDataRepository.Get(token); - var valuesIdentity = valuesIdentities? - .FirstOrDefault(e => e.DiscriminatorId == discriminatorId); - if (valuesIdentity == null) - return []; - - var entities = await query.ToArrayAsync(token); - - var dtos = entities.Select(entity => - { - var dto = new TimestampedValuesDto() - { - Timestamp = entity.Timestamp.ToUniversalTime() - }; - - for (var i = 0; i < valuesIdentity.Identity.Count(); i++) - { - var key = valuesIdentity.Identity[i]; - var value = entity.Values[i]; - - dto.Values.Add(key, value); - } - - return dto; - }); - - return dtos; - } - + /// + /// Применить фильтр по дате + /// + /// + /// + /// private IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset timestampBegin) { var geTimestampUtc = timestampBegin.ToUniversalTime(); @@ -196,46 +175,4 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository return result; } - - private IEnumerable ReduceSetColumnsByNames(IEnumerable dtos, IEnumerable columnNames) - { - var result = dtos.Select(dto => - { - var reducedValues = dto.Values - .Where(v => columnNames.Contains(v.Key)) - .ToDictionary(); - dto.Values = reducedValues; - - return dto; - }); - - return result; - } - - private async Task CreateValuesIdentityIfNotExist(Guid discriminatorId, string[] keys, CancellationToken token) - { - var valuesIdentities = await relatedDataRepository.Get(token); - var valuesIdentity = valuesIdentities? - .FirstOrDefault(e => e.DiscriminatorId == discriminatorId); - - if (valuesIdentity is null) - { - valuesIdentity = new ValuesIdentityDto() - { - DiscriminatorId = discriminatorId, - Identity = keys - }; - await relatedDataRepository.Add(valuesIdentity, token); - - return; - } - - if (!valuesIdentity.Identity.SequenceEqual(keys)) - { - var expectedIdentity = string.Join(", ", valuesIdentity.Identity); - var actualIdentity = string.Join(", ", keys); - throw new InvalidOperationException($"Для системы {discriminatorId.ToString()} " + - $"характерен набор данных: [{expectedIdentity}], однако был передан набор: [{actualIdentity}]"); - } - } } diff --git a/DD.Persistence.Repository/RepositoriesCached/DataSchemeCachedRepository.cs b/DD.Persistence.Repository/RepositoriesCached/DataSchemeCachedRepository.cs new file mode 100644 index 0000000..aeb144a --- /dev/null +++ b/DD.Persistence.Repository/RepositoriesCached/DataSchemeCachedRepository.cs @@ -0,0 +1,30 @@ +using DD.Persistence.Models; +using DD.Persistence.Repository.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; + +namespace DD.Persistence.Repository.RepositoriesCached; +public class DataSchemeCachedRepository : DataSchemeRepository +{ + private readonly IMemoryCache memoryCache; + + public DataSchemeCachedRepository(DbContext db, IMemoryCache memoryCache) : base(db) + { + this.memoryCache = memoryCache; + } + + public override async Task Add(DataSchemeDto dataSourceSystemDto, CancellationToken token) + { + await base.Add(dataSourceSystemDto, token); + + memoryCache.Set(dataSourceSystemDto.DiscriminatorId, dataSourceSystemDto); + } + + public override async Task GetByDiscriminator(Guid discriminatorId, CancellationToken token) + { + var result = memoryCache.Get(discriminatorId) + ?? await base.GetByDiscriminator(discriminatorId, token); + + return result; + } +} diff --git a/DD.Persistence.Repository/RepositoriesCached/RelatedDataCachedRepository.cs b/DD.Persistence.Repository/RepositoriesCached/DataSourceSystemCachedRepository.cs similarity index 56% rename from DD.Persistence.Repository/RepositoriesCached/RelatedDataCachedRepository.cs rename to DD.Persistence.Repository/RepositoriesCached/DataSourceSystemCachedRepository.cs index 6f596ba..87e487e 100644 --- a/DD.Persistence.Repository/RepositoriesCached/RelatedDataCachedRepository.cs +++ b/DD.Persistence.Repository/RepositoriesCached/DataSourceSystemCachedRepository.cs @@ -1,27 +1,27 @@ -using DD.Persistence.Repository.Repositories; +using DD.Persistence.Database.Entity; +using DD.Persistence.Models; +using DD.Persistence.Repository.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; namespace DD.Persistence.Repository.RepositoriesCached; -public class RelatedDataCachedRepository : RelatedDataRepository - where TEntity : class, new() - where TDto : class, new() +public class DataSourceSystemCachedRepository : DataSourceSystemRepository { - private static readonly string SystemCacheKey = $"{typeof(TEntity).FullName}CacheKey"; + private static readonly string SystemCacheKey = $"{typeof(DataSourceSystem).FullName}CacheKey"; private readonly IMemoryCache memoryCache; private readonly TimeSpan? AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60); - public RelatedDataCachedRepository(DbContext db, IMemoryCache memoryCache) : base(db) + public DataSourceSystemCachedRepository(DbContext db, IMemoryCache memoryCache) : base(db) { this.memoryCache = memoryCache; } - public override async Task Add(TDto dataSourceSystemDto, CancellationToken token) + public override async Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token) { await base.Add(dataSourceSystemDto, token); memoryCache.Remove(SystemCacheKey); } - public override async Task> Get(CancellationToken token) + public override async Task> Get(CancellationToken token) { var systems = await memoryCache.GetOrCreateAsync(SystemCacheKey, async (cacheEntry) => { diff --git a/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs b/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs index 7bdaa98..f81f7a0 100644 --- a/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs +++ b/DD.Persistence.Repository/RepositoriesCached/TimestampedValuesCachedRepository.cs @@ -12,7 +12,7 @@ // private const int CacheItemsCount = 3600; -// public TimestampedValuesCachedRepository(DbContext db, IRelatedDataRepository relatedDataRepository) : base(db, relatedDataRepository) +// public TimestampedValuesCachedRepository(DbContext db, IDataSourceSystemRepository relatedDataRepository) : base(db, relatedDataRepository) // { // //Task.Run(async () => // //{ diff --git a/DD.Persistence/EFExtensions.cs b/DD.Persistence/Extensions/EFExtensions.cs similarity index 98% rename from DD.Persistence/EFExtensions.cs rename to DD.Persistence/Extensions/EFExtensions.cs index af6cd15..f1d1fb8 100644 --- a/DD.Persistence/EFExtensions.cs +++ b/DD.Persistence/Extensions/EFExtensions.cs @@ -2,7 +2,7 @@ using System.Linq.Expressions; using System.Reflection; -namespace DD.Persistence; +namespace DD.Persistence.Extensions; public static class EFExtensions { struct TypeAccessor @@ -23,7 +23,7 @@ public static class EFExtensions private static ConcurrentDictionary> TypePropSelectors { get; set; } = new(); private static MethodInfo GetExtOrderMethod(string methodName) - => typeof(System.Linq.Queryable) + => typeof(Queryable) .GetMethods() .Where(m => m.Name == methodName && m.IsGenericMethodDefinition && diff --git a/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs b/DD.Persistence/Extensions/IEnumerableExtensions.cs similarity index 93% rename from DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs rename to DD.Persistence/Extensions/IEnumerableExtensions.cs index e307513..702a08b 100644 --- a/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs +++ b/DD.Persistence/Extensions/IEnumerableExtensions.cs @@ -1,4 +1,4 @@ -namespace DD.Persistence.Repository.Extensions; +namespace DD.Persistence.Extensions; public static class IEnumerableExtensions { diff --git a/DD.Persistence/Repositories/IDataSchemeRepository.cs b/DD.Persistence/Repositories/IDataSchemeRepository.cs new file mode 100644 index 0000000..ff6ef4b --- /dev/null +++ b/DD.Persistence/Repositories/IDataSchemeRepository.cs @@ -0,0 +1,25 @@ +using DD.Persistence.Models; + +namespace DD.Persistence.Repositories; + +/// +/// Репозиторий для работы со схемами наборов данных +/// +public interface IDataSchemeRepository +{ + /// + /// Добавить схему + /// + /// + /// + /// + Task Add(DataSchemeDto dataSourceSystemDto, CancellationToken token); + + /// + /// Вычитать схему + /// + /// Дискриминатор системы + /// + /// + Task GetByDiscriminator(Guid discriminatorId, CancellationToken token); +} \ No newline at end of file diff --git a/DD.Persistence/Repositories/IDataSourceSystemRepository.cs b/DD.Persistence/Repositories/IDataSourceSystemRepository.cs new file mode 100644 index 0000000..21d7b1c --- /dev/null +++ b/DD.Persistence/Repositories/IDataSourceSystemRepository.cs @@ -0,0 +1,23 @@ +using DD.Persistence.Models; + +namespace DD.Persistence.Repositories; + +/// +/// Репозиторий для работы с системами - источниками данных +/// +public interface IDataSourceSystemRepository +{ + /// + /// Добавить систему - источник данных + /// + /// + /// + /// + public Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token); + + /// + /// Получить список систем - источников данных + /// + /// + public Task> Get(CancellationToken token); +} diff --git a/DD.Persistence/Repositories/IRelatedDataRepository.cs b/DD.Persistence/Repositories/IRelatedDataRepository.cs deleted file mode 100644 index e7eae87..0000000 --- a/DD.Persistence/Repositories/IRelatedDataRepository.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace DD.Persistence.Repositories; - -/// -/// Интерфейс по работе с простой структурой данных, подразумевающей наличие связи с более сложной -/// В контексте TechMessagesRepository это системы - источники данных -/// В контексте TimestampedValuesRepository это идентификационные наборы (ключи для значений в соответствии с индексами в хранимых массивах) -/// -public interface IRelatedDataRepository -{ - /// - /// Добавить данные - /// - /// - /// - /// - public Task Add(TDto dataSourceSystemDto, CancellationToken token); - - /// - /// Получить список данных - /// - /// - public Task> Get(CancellationToken token); -} diff --git a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs index abb860c..6bd2866 100644 --- a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs +++ b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs @@ -4,14 +4,14 @@ using DD.Persistence.RepositoriesAbstractions; namespace DD.Persistence.Repositories; /// -/// Интерфейс по работе с временными данными +/// Репозиторий для работы с временными данными /// public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBaseRepository { /// /// Добавление записей /// - /// + /// Дискриминатор (идентификатор) набора /// /// /// @@ -35,7 +35,7 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase /// /// /// - Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); + Task>> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); /// /// Получение данных с начала @@ -44,7 +44,7 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase /// Количество /// /// - Task> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token); + Task>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token); /// /// Получение данных с конца @@ -53,5 +53,5 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase /// Количество /// /// - Task> GetLast(Guid discriminatorId, int takeCount, CancellationToken token); + Task>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token); } diff --git a/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs b/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs index d60e0ce..95bfee2 100644 --- a/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs +++ b/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs @@ -6,7 +6,7 @@ namespace DD.Persistence.RepositoriesAbstractions; /// /// Интерфейс по работе с данными /// -public interface ISyncRepository +public interface ISyncRepository // ToDo: исчерпывающая абстракция { /// /// Получить данные, начиная с определенной даты @@ -15,7 +15,7 @@ public interface ISyncRepository /// дата начала /// /// - Task> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token); + Task>> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token); /// diff --git a/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs b/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs index b8c6791..04b8e1a 100644 --- a/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs +++ b/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs @@ -5,7 +5,7 @@ namespace DD.Persistence.RepositoriesAbstractions; /// /// Интерфейс по работе с прореженными данными /// -public interface ITimeSeriesBaseRepository +public interface ITimeSeriesBaseRepository // ToDo: исчерпывающая абстракция { /// /// Получить список объектов с прореживанием @@ -16,7 +16,7 @@ public interface ITimeSeriesBaseRepository /// /// /// - Task> GetResampledData( + Task>> GetResampledData( Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, diff --git a/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs b/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs new file mode 100644 index 0000000..8e6d606 --- /dev/null +++ b/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs @@ -0,0 +1,85 @@ +using DD.Persistence.Models; +using DD.Persistence.Models.Common; + +namespace DD.Persistence.Services.Interfaces; + +/// +/// Сервис для работы с временными данными +/// +public interface ITimestampedValuesService +{ + /// + /// Добавление записей + /// + /// + /// + /// + /// + Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token); + + /// + /// Количество записей по указанному набору в БД. Для пагинации + /// + /// Дискриминатор (идентификатор) набора + /// + /// + Task Count(Guid discriminatorId, CancellationToken token); + + /// + /// Получение данных с фильтрацией. Значение фильтра null - отключен + /// + /// Дискриминатор (идентификатор) набора + /// Фильтр позднее даты + /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора + /// + /// + /// + /// + Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); + + /// + /// Получение данных с начала + /// + /// Дискриминатор (идентификатор) набора + /// Количество + /// + /// + Task> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token); + + /// + /// Получить данные, начиная с определенной даты + /// + /// + /// дата начала + /// + /// + Task> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token); + + /// + /// Получение данных с конца + /// + /// Дискриминатор (идентификатор) набора + /// Количество + /// + /// + Task> GetLast(Guid discriminatorId, int takeCount, CancellationToken token); + + /// + /// Получить список объектов с прореживанием + /// + /// + /// дата начала + /// + /// + /// + /// + Task> GetResampledData(Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default); + + /// + /// Получить диапазон дат, для которых есть данные + /// + /// + /// + /// + Task GetDatesRange(Guid discriminatorId, CancellationToken token); +} \ No newline at end of file diff --git a/DD.Persistence/Services/TimestampedValuesService.cs b/DD.Persistence/Services/TimestampedValuesService.cs new file mode 100644 index 0000000..2b7fc6f --- /dev/null +++ b/DD.Persistence/Services/TimestampedValuesService.cs @@ -0,0 +1,201 @@ +using DD.Persistence.Extensions; +using DD.Persistence.Models; +using DD.Persistence.Models.Common; +using DD.Persistence.Repositories; +using DD.Persistence.Services.Interfaces; + +namespace DD.Persistence.Services; + +/// +public class TimestampedValuesService : ITimestampedValuesService +{ + private readonly ITimestampedValuesRepository timestampedValuesRepository; + private readonly IDataSchemeRepository dataSchemeRepository; + + /// + public TimestampedValuesService(ITimestampedValuesRepository timestampedValuesRepository, IDataSchemeRepository relatedDataRepository) + { + this.timestampedValuesRepository = timestampedValuesRepository; + this.dataSchemeRepository = relatedDataRepository; + } + + /// + public async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) + { + foreach (var dto in dtos) + { + var keys = dto.Values.Keys.ToArray(); + await CreateSystemSpecificationIfNotExist(discriminatorId, keys, token); + } + + var result = await timestampedValuesRepository.AddRange(discriminatorId, dtos, token); + + return result; + } + + /// + public async Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + { + var result = await timestampedValuesRepository.Get(discriminatorId, geTimestamp, columnNames, skip, take, token); + + var dtos = await Materialize(discriminatorId, result, token); + + if (!columnNames.IsNullOrEmpty()) + { + dtos = ReduceSetColumnsByNames(dtos, columnNames!); + } + + return dtos; + } + + /// + public async Task> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token) + { + var result = await timestampedValuesRepository.GetFirst(discriminatorId, takeCount, token); + + var dtos = await Materialize(discriminatorId, result, token); + + return dtos; + } + + /// + public async Task> GetLast(Guid discriminatorId, int takeCount, CancellationToken token) + { + var result = await timestampedValuesRepository.GetLast(discriminatorId, takeCount, token); + + var dtos = await Materialize(discriminatorId, result, token); + + return dtos; + } + + /// + public async Task> GetResampledData( + Guid discriminatorId, + DateTimeOffset beginTimestamp, + double intervalSec = 600d, + int approxPointsCount = 1024, + CancellationToken token = default) + { + var result = await timestampedValuesRepository.GetResampledData(discriminatorId, beginTimestamp, intervalSec, approxPointsCount, token); + + var dtos = await Materialize(discriminatorId, result, token); + + return dtos; + } + + /// + public async Task> GetGtDate(Guid discriminatorId, DateTimeOffset beginTimestamp, CancellationToken token) + { + var result = await timestampedValuesRepository.GetGtDate(discriminatorId, beginTimestamp, token); + + var dtos = await Materialize(discriminatorId, result, token); + + return dtos; + } + + /// + public async Task Count(Guid discriminatorId, CancellationToken token) + { + var result = await timestampedValuesRepository.Count(discriminatorId, token); + + return result; + } + + /// + public async virtual Task GetDatesRange(Guid discriminatorId, CancellationToken token) + { + var result = await timestampedValuesRepository.GetDatesRange(discriminatorId, token); + + return result; + } + + /// + /// Преобразовать результат запроса в набор dto + /// + /// + /// + /// + /// + private async Task> Materialize(Guid discriminatorId, IEnumerable> queryResult, CancellationToken token) + { + var systemSpecification = await dataSchemeRepository.GetByDiscriminator(discriminatorId, token); + if (systemSpecification is null) + { + return []; + } + + var dtos = queryResult.Select(entity => + { + var dto = new TimestampedValuesDto() + { + Timestamp = entity.Item1.ToUniversalTime() + }; + + var identity = systemSpecification!.PropNames; + for (var i = 0; i < identity.Count(); i++) + { + var key = identity[i]; + var value = entity.Item2[i]; + + dto.Values.Add(key, value); + } + + return dto; + }); + + return dtos; + } + + /// + /// Создать спецификацию, при отсутствии таковой + /// + /// Дискриминатор системы + /// Набор наименований полей + /// + /// + /// Некорректный набор наименований полей + private async Task CreateSystemSpecificationIfNotExist(Guid discriminatorId, string[] fieldNames, CancellationToken token) + { + var systemSpecification = await dataSchemeRepository.GetByDiscriminator(discriminatorId, token); + if (systemSpecification is null) + { + systemSpecification = new DataSchemeDto() + { + DiscriminatorId = discriminatorId, + PropNames = fieldNames + }; + await dataSchemeRepository.Add(systemSpecification, token); + + return; + } + + if (!systemSpecification.PropNames.SequenceEqual(fieldNames)) + { + var expectedFieldNames = string.Join(", ", systemSpecification.PropNames); + var actualFieldNames = string.Join(", ", fieldNames); + throw new InvalidOperationException($"Для системы {discriminatorId.ToString()} " + + $"характерен набор данных: [{expectedFieldNames}], однако был передан набор: [{actualFieldNames}]"); + } + } + + /// + /// Отсеить лишние поля в соответствии с заданным фильтром + /// + /// + /// Поля, которые необходимо оставить + /// + private IEnumerable ReduceSetColumnsByNames(IEnumerable dtos, IEnumerable fieldNames) + { + var result = dtos.Select(dto => + { + var reducedValues = dto.Values + .Where(v => fieldNames.Contains(v.Key)) + .ToDictionary(); + dto.Values = reducedValues; + + return dto; + }); + + return result; + } +} -- 2.45.2 From c16cc21384ce3e371e5e63724319bfc91b7f3351 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Tue, 21 Jan 2025 10:35:30 +0500 Subject: [PATCH 12/16] Auto stash before merge of "TimestampedValuesRepository" and "master" --- .../DD.Persistence.Test.csproj | 21 +++++++++++++++++++ .../TimestampedValuesServiceShould.cs | 19 +++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 DD.Persistence.Test/DD.Persistence.Test.csproj create mode 100644 DD.Persistence.Test/TimestampedValuesServiceShould.cs diff --git a/DD.Persistence.Test/DD.Persistence.Test.csproj b/DD.Persistence.Test/DD.Persistence.Test.csproj new file mode 100644 index 0000000..9135b1c --- /dev/null +++ b/DD.Persistence.Test/DD.Persistence.Test.csproj @@ -0,0 +1,21 @@ + + + + net9.0 + enable + enable + false + + + + + + + + + + + + + + diff --git a/DD.Persistence.Test/TimestampedValuesServiceShould.cs b/DD.Persistence.Test/TimestampedValuesServiceShould.cs new file mode 100644 index 0000000..ea19171 --- /dev/null +++ b/DD.Persistence.Test/TimestampedValuesServiceShould.cs @@ -0,0 +1,19 @@ +using DD.Persistence.Repositories; +using DD.Persistence.Services; +using NSubstitute; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DD.Persistence.Repository.Test; +public class TimestampedValuesServiceShould +{ + private readonly ITimestampedValuesRepository timestampedValuesRepository = Substitute.For(); + private readonly IDataSchemeRepository dataSchemeRepository = Substitute.For(); + public TimestampedValuesServiceShould() + { + var timestampedValuesService = new TimestampedValuesService(timestampedValuesRepository, dataSchemeRepository); + } +} -- 2.45.2 From e981b835a5e8ba476aa0664fd9236b432eb0f972 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Tue, 21 Jan 2025 11:32:17 +0500 Subject: [PATCH 13/16] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=BE=D1=81=D0=BF=D0=BE=D1=81=D0=BE=D0=B1?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=20TimestampedValuesService=20=D0=BD=D0=B5=D0=B7?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE=20=D0=BE=D1=82=20?= =?UTF-8?q?=D1=80=D0=B5=D0=BF=D0=BE=D0=B7=D0=B8=D1=82=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=B5=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DD.Persistence.Test.csproj | 5 ++ .../TimestampedValuesServiceShould.cs | 53 ++++++++++++++++--- DD.Persistence.sln | 6 +++ 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/DD.Persistence.Test/DD.Persistence.Test.csproj b/DD.Persistence.Test/DD.Persistence.Test.csproj index 9135b1c..8efaa34 100644 --- a/DD.Persistence.Test/DD.Persistence.Test.csproj +++ b/DD.Persistence.Test/DD.Persistence.Test.csproj @@ -10,10 +10,15 @@ + + + + + diff --git a/DD.Persistence.Test/TimestampedValuesServiceShould.cs b/DD.Persistence.Test/TimestampedValuesServiceShould.cs index ea19171..5f3e65b 100644 --- a/DD.Persistence.Test/TimestampedValuesServiceShould.cs +++ b/DD.Persistence.Test/TimestampedValuesServiceShould.cs @@ -1,19 +1,58 @@ -using DD.Persistence.Repositories; +using DD.Persistence.Models; +using DD.Persistence.Repositories; using DD.Persistence.Services; using NSubstitute; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace DD.Persistence.Repository.Test; public class TimestampedValuesServiceShould { private readonly ITimestampedValuesRepository timestampedValuesRepository = Substitute.For(); private readonly IDataSchemeRepository dataSchemeRepository = Substitute.For(); + private TimestampedValuesService timestampedValuesService; + public TimestampedValuesServiceShould() { - var timestampedValuesService = new TimestampedValuesService(timestampedValuesRepository, dataSchemeRepository); + timestampedValuesService = new TimestampedValuesService(timestampedValuesRepository, dataSchemeRepository); + } + + [Fact] + public async Task TestServiceEfficiency() + { + var discriminatorId = Guid.NewGuid(); + const int count = 10; + var dtos = Generate(count, DateTimeOffset.UtcNow); + var addRangeResult = await timestampedValuesService + .AddRange(discriminatorId, dtos, CancellationToken.None); + Assert.Equal(0, addRangeResult); + + var columnNames = new[] { "A", "B", "C", "D" }; + var geTimestamp = DateTimeOffset.UtcNow + .AddHours(-1) + .ToUniversalTime(); + var getResult = await timestampedValuesService + .Get(discriminatorId, geTimestamp, columnNames, 0, count, CancellationToken.None); + Assert.NotNull(getResult); + Assert.Empty(getResult); + } + + private static IEnumerable Generate(int countToCreate, DateTimeOffset from) + { + var result = new List(); + for (int i = 0; i < countToCreate; i++) + { + var values = new Dictionary() + { + { "A", i }, + { "B", i * 1.1 }, + { "C", $"Any{i}" }, + { "D", DateTimeOffset.Now }, + }; + + yield return new TimestampedValuesDto() + { + Timestamp = from.AddSeconds(i), + Values = values + }; + } } } diff --git a/DD.Persistence.sln b/DD.Persistence.sln index eeb198c..03f0be9 100644 --- a/DD.Persistence.sln +++ b/DD.Persistence.sln @@ -28,6 +28,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt Directory.Build.props = Directory.Build.props EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DD.Persistence.Test", "DD.Persistence.Test\DD.Persistence.Test.csproj", "{B8C774E6-6B75-41AC-B3CF-10BD3623B2FA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -74,6 +76,10 @@ Global {08B03623-A1C9-482F-B60E-09F293E04999}.Debug|Any CPU.Build.0 = Debug|Any CPU {08B03623-A1C9-482F-B60E-09F293E04999}.Release|Any CPU.ActiveCfg = Release|Any CPU {08B03623-A1C9-482F-B60E-09F293E04999}.Release|Any CPU.Build.0 = Release|Any CPU + {B8C774E6-6B75-41AC-B3CF-10BD3623B2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8C774E6-6B75-41AC-B3CF-10BD3623B2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8C774E6-6B75-41AC-B3CF-10BD3623B2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8C774E6-6B75-41AC-B3CF-10BD3623B2FA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE -- 2.45.2 From 3262201973a7e9f9233f6779073e4b5d72ca8109 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 22 Jan 2025 15:16:24 +0500 Subject: [PATCH 14/16] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B7=D1=83=D0=BB=D1=8C=D1=82=D0=B0?= =?UTF-8?q?=D1=82=D0=B0=D0=BC=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/TimestampedValuesController.cs | 2 +- DD.Persistence.Client/Clients/Base/BaseClient.cs | 4 ++-- .../Clients/Interfaces/ITimestampedValuesClient.cs | 1 - .../Repositories/TimestampedValuesRepository.cs | 1 + DD.Persistence/Services/TimestampedValuesService.cs | 12 +++++------- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/DD.Persistence.API/Controllers/TimestampedValuesController.cs b/DD.Persistence.API/Controllers/TimestampedValuesController.cs index 759d88a..abc1114 100644 --- a/DD.Persistence.API/Controllers/TimestampedValuesController.cs +++ b/DD.Persistence.API/Controllers/TimestampedValuesController.cs @@ -35,7 +35,7 @@ public class TimestampedValuesController : ControllerBase { var result = await timestampedValuesRepository.AddRange(discriminatorId, dtos, token); - return Ok(result); + return CreatedAtAction(nameof(AddRange), result); } /// diff --git a/DD.Persistence.Client/Clients/Base/BaseClient.cs b/DD.Persistence.Client/Clients/Base/BaseClient.cs index f9bcdd7..3b13cdb 100644 --- a/DD.Persistence.Client/Clients/Base/BaseClient.cs +++ b/DD.Persistence.Client/Clients/Base/BaseClient.cs @@ -12,13 +12,13 @@ public abstract class BaseClient this.logger = logger; } - public async Task ExecuteGetResponse(Func>> getMethod, CancellationToken token) + public async Task ExecuteGetResponse(Func>> getMethod, CancellationToken token) { var response = await getMethod.Invoke().WaitAsync(token); if (response.IsSuccessStatusCode) { - return response.Content!; + return response.Content; } var exception = response.GetPersistenceException(); diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs index 9c9f262..b0ea180 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs @@ -96,7 +96,6 @@ public interface ITimestampedValuesClient : IDisposable /// /// /// - /// /// /// /// diff --git a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs index b2f8203..204b1a2 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs @@ -92,6 +92,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository return result; } + // ToDo: прореживание должно осуществляться до материализации public async virtual Task>> GetResampledData( Guid discriminatorId, DateTimeOffset dateBegin, diff --git a/DD.Persistence/Services/TimestampedValuesService.cs b/DD.Persistence/Services/TimestampedValuesService.cs index 2b7fc6f..d6d6bf4 100644 --- a/DD.Persistence/Services/TimestampedValuesService.cs +++ b/DD.Persistence/Services/TimestampedValuesService.cs @@ -22,6 +22,7 @@ public class TimestampedValuesService : ITimestampedValuesService /// public async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) { + // ToDo: реализовать без foreach foreach (var dto in dtos) { var keys = dto.Values.Keys.ToArray(); @@ -132,13 +133,10 @@ public class TimestampedValuesService : ITimestampedValuesService }; var identity = systemSpecification!.PropNames; - for (var i = 0; i < identity.Count(); i++) - { - var key = identity[i]; - var value = entity.Item2[i]; - - dto.Values.Add(key, value); - } + var indexedIdentity = identity + .Select((value, index) => new { index, value }); + dto.Values = indexedIdentity + .ToDictionary(x => x.value, x => entity.Item2[x.index]); return dto; }); -- 2.45.2 From 4af65e258bd50f5d2d0c7fb8496ff39210041614 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 22 Jan 2025 16:13:56 +0500 Subject: [PATCH 15/16] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ner.cs => 20250122111321_Init.Designer.cs} | 44 ++++++++--------- ...6093615_Init.cs => 20250122111321_Init.cs} | 49 ++++++++++--------- ...PersistencePostgresContextModelSnapshot.cs | 42 ++++++++-------- DD.Persistence.Database/Entity/DataScheme.cs | 2 +- .../Repositories/DataSchemeRepository.cs | 4 +- .../DataSchemeCachedRepository.cs | 4 +- .../Repositories/IDataSchemeRepository.cs | 4 +- .../Services/TimestampedValuesService.cs | 12 ++--- 8 files changed, 81 insertions(+), 80 deletions(-) rename DD.Persistence.Database.Postgres/Migrations/{20250116093615_Init.Designer.cs => 20250122111321_Init.Designer.cs} (94%) rename DD.Persistence.Database.Postgres/Migrations/{20250116093615_Init.cs => 20250122111321_Init.cs} (92%) diff --git a/DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.Designer.cs b/DD.Persistence.Database.Postgres/Migrations/20250122111321_Init.Designer.cs similarity index 94% rename from DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.Designer.cs rename to DD.Persistence.Database.Postgres/Migrations/20250122111321_Init.Designer.cs index 0fa9552..a3678d6 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.Designer.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250122111321_Init.Designer.cs @@ -1,5 +1,6 @@ // using System; +using System.Text.Json; using DD.Persistence.Database.Model; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -12,7 +13,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace DD.Persistence.Database.Postgres.Migrations { [DbContext(typeof(PersistencePostgresContext))] - [Migration("20250116093615_Init")] + [Migration("20250122111321_Init")] partial class Init { /// @@ -25,6 +26,23 @@ namespace DD.Persistence.Database.Postgres.Migrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("DD.Persistence.Database.Entity.DataScheme", b => + { + b.Property("DiscriminatorId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasComment("Идентификатор схемы данных"); + + b.Property("PropNames") + .IsRequired() + .HasColumnType("jsonb") + .HasComment("Наименования полей в порядке индексации"); + + b.HasKey("DiscriminatorId"); + + b.ToTable("DataSchemes"); + }); + modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b => { b.Property("SystemId") @@ -125,23 +143,6 @@ namespace DD.Persistence.Database.Postgres.Migrations b.ToTable("TimestampedValues"); }); - modelBuilder.Entity("DD.Persistence.Database.Entity.ValuesIdentity", b => - { - b.Property("DiscriminatorId") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasComment("Дискриминатор системы"); - - b.Property("Identity") - .IsRequired() - .HasColumnType("jsonb") - .HasComment("Идентификаторы"); - - b.HasKey("DiscriminatorId"); - - b.ToTable("ValuesIdentities"); - }); - modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b => { b.Property("Id") @@ -209,8 +210,7 @@ namespace DD.Persistence.Database.Postgres.Migrations .HasColumnType("uuid") .HasComment("Id автора последнего изменения"); - b.Property("Value") - .IsRequired() + b.Property("Value") .HasColumnType("jsonb") .HasComment("Значение уставки"); @@ -232,13 +232,13 @@ namespace DD.Persistence.Database.Postgres.Migrations modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b => { - b.HasOne("DD.Persistence.Database.Entity.ValuesIdentity", "ValuesIdentity") + b.HasOne("DD.Persistence.Database.Entity.DataScheme", "DataScheme") .WithMany() .HasForeignKey("DiscriminatorId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("ValuesIdentity"); + b.Navigation("DataScheme"); }); #pragma warning restore 612, 618 } diff --git a/DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.cs b/DD.Persistence.Database.Postgres/Migrations/20250122111321_Init.cs similarity index 92% rename from DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.cs rename to DD.Persistence.Database.Postgres/Migrations/20250122111321_Init.cs index 1898472..6f5ecfc 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20250116093615_Init.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250122111321_Init.cs @@ -1,4 +1,5 @@ using System; +using System.Text.Json; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -32,6 +33,18 @@ namespace DD.Persistence.Database.Postgres.Migrations table.PrimaryKey("PK_ChangeLog", x => x.Id); }); + migrationBuilder.CreateTable( + name: "DataSchemes", + columns: table => new + { + DiscriminatorId = table.Column(type: "uuid", nullable: false, comment: "Идентификатор схемы данных"), + PropNames = table.Column(type: "jsonb", nullable: false, comment: "Наименования полей в порядке индексации") + }, + constraints: table => + { + table.PrimaryKey("PK_DataSchemes", x => x.DiscriminatorId); + }); + migrationBuilder.CreateTable( name: "DataSourceSystem", columns: table => new @@ -65,7 +78,7 @@ namespace DD.Persistence.Database.Postgres.Migrations { Key = table.Column(type: "uuid", nullable: false, comment: "Ключ"), Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Дата создания уставки"), - Value = table.Column(type: "jsonb", nullable: false, comment: "Значение уставки"), + Value = table.Column(type: "jsonb", nullable: false, comment: "Значение уставки"), IdUser = table.Column(type: "uuid", nullable: false, comment: "Id автора последнего изменения") }, constraints: table => @@ -74,15 +87,22 @@ namespace DD.Persistence.Database.Postgres.Migrations }); migrationBuilder.CreateTable( - name: "ValuesIdentities", + name: "TimestampedValues", columns: table => new { + Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Временная отметка"), DiscriminatorId = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор системы"), - Identity = table.Column(type: "jsonb", nullable: false, comment: "Идентификаторы") + Values = table.Column(type: "jsonb", nullable: false, comment: "Данные") }, constraints: table => { - table.PrimaryKey("PK_ValuesIdentities", x => x.DiscriminatorId); + table.PrimaryKey("PK_TimestampedValues", x => new { x.DiscriminatorId, x.Timestamp }); + table.ForeignKey( + name: "FK_TimestampedValues_DataSchemes_DiscriminatorId", + column: x => x.DiscriminatorId, + principalTable: "DataSchemes", + principalColumn: "DiscriminatorId", + onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( @@ -107,25 +127,6 @@ namespace DD.Persistence.Database.Postgres.Migrations onDelete: ReferentialAction.Cascade); }); - migrationBuilder.CreateTable( - name: "TimestampedValues", - columns: table => new - { - Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Временная отметка"), - DiscriminatorId = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор системы"), - Values = table.Column(type: "jsonb", nullable: false, comment: "Данные") - }, - constraints: table => - { - table.PrimaryKey("PK_TimestampedValues", x => new { x.DiscriminatorId, x.Timestamp }); - table.ForeignKey( - name: "FK_TimestampedValues_ValuesIdentities_DiscriminatorId", - column: x => x.DiscriminatorId, - principalTable: "ValuesIdentities", - principalColumn: "DiscriminatorId", - onDelete: ReferentialAction.Cascade); - }); - migrationBuilder.CreateIndex( name: "IX_TechMessage_SystemId", table: "TechMessage", @@ -154,7 +155,7 @@ namespace DD.Persistence.Database.Postgres.Migrations name: "DataSourceSystem"); migrationBuilder.DropTable( - name: "ValuesIdentities"); + name: "DataSchemes"); } } } diff --git a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs index 72e5d62..7171bb8 100644 --- a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs +++ b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs @@ -1,5 +1,6 @@ // using System; +using System.Text.Json; using DD.Persistence.Database.Model; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -22,6 +23,23 @@ namespace DD.Persistence.Database.Postgres.Migrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("DD.Persistence.Database.Entity.DataScheme", b => + { + b.Property("DiscriminatorId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasComment("Идентификатор схемы данных"); + + b.Property("PropNames") + .IsRequired() + .HasColumnType("jsonb") + .HasComment("Наименования полей в порядке индексации"); + + b.HasKey("DiscriminatorId"); + + b.ToTable("DataSchemes"); + }); + modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b => { b.Property("SystemId") @@ -122,23 +140,6 @@ namespace DD.Persistence.Database.Postgres.Migrations b.ToTable("TimestampedValues"); }); - modelBuilder.Entity("DD.Persistence.Database.Entity.ValuesIdentity", b => - { - b.Property("DiscriminatorId") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasComment("Дискриминатор системы"); - - b.Property("Identity") - .IsRequired() - .HasColumnType("jsonb") - .HasComment("Идентификаторы"); - - b.HasKey("DiscriminatorId"); - - b.ToTable("ValuesIdentities"); - }); - modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b => { b.Property("Id") @@ -206,8 +207,7 @@ namespace DD.Persistence.Database.Postgres.Migrations .HasColumnType("uuid") .HasComment("Id автора последнего изменения"); - b.Property("Value") - .IsRequired() + b.Property("Value") .HasColumnType("jsonb") .HasComment("Значение уставки"); @@ -229,13 +229,13 @@ namespace DD.Persistence.Database.Postgres.Migrations modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b => { - b.HasOne("DD.Persistence.Database.Entity.ValuesIdentity", "ValuesIdentity") + b.HasOne("DD.Persistence.Database.Entity.DataScheme", "DataScheme") .WithMany() .HasForeignKey("DiscriminatorId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("ValuesIdentity"); + b.Navigation("DataScheme"); }); #pragma warning restore 612, 618 } diff --git a/DD.Persistence.Database/Entity/DataScheme.cs b/DD.Persistence.Database/Entity/DataScheme.cs index 7936d0c..7fa3394 100644 --- a/DD.Persistence.Database/Entity/DataScheme.cs +++ b/DD.Persistence.Database/Entity/DataScheme.cs @@ -6,7 +6,7 @@ namespace DD.Persistence.Database.Entity; public class DataScheme { - [Key, Comment("Дискриминатор системы"),] + [Key, Comment("Идентификатор схемы данных"),] public Guid DiscriminatorId { get; set; } [Comment("Наименования полей в порядке индексации"), Column(TypeName = "jsonb")] diff --git a/DD.Persistence.Repository/Repositories/DataSchemeRepository.cs b/DD.Persistence.Repository/Repositories/DataSchemeRepository.cs index 3fe99d2..c30c8da 100644 --- a/DD.Persistence.Repository/Repositories/DataSchemeRepository.cs +++ b/DD.Persistence.Repository/Repositories/DataSchemeRepository.cs @@ -22,10 +22,10 @@ public class DataSchemeRepository : IDataSchemeRepository await db.SaveChangesAsync(token); } - public virtual async Task GetByDiscriminator(Guid discriminatorId, CancellationToken token) + public virtual async Task Get(Guid dataSchemeId, CancellationToken token) { var query = GetQueryReadOnly() - .Where(e => e.DiscriminatorId == discriminatorId); + .Where(e => e.DiscriminatorId == dataSchemeId); var entity = await query.ToArrayAsync(); var dto = entity.Select(e => e.Adapt()).FirstOrDefault(); diff --git a/DD.Persistence.Repository/RepositoriesCached/DataSchemeCachedRepository.cs b/DD.Persistence.Repository/RepositoriesCached/DataSchemeCachedRepository.cs index aeb144a..ea22196 100644 --- a/DD.Persistence.Repository/RepositoriesCached/DataSchemeCachedRepository.cs +++ b/DD.Persistence.Repository/RepositoriesCached/DataSchemeCachedRepository.cs @@ -20,10 +20,10 @@ public class DataSchemeCachedRepository : DataSchemeRepository memoryCache.Set(dataSourceSystemDto.DiscriminatorId, dataSourceSystemDto); } - public override async Task GetByDiscriminator(Guid discriminatorId, CancellationToken token) + public override async Task Get(Guid discriminatorId, CancellationToken token) { var result = memoryCache.Get(discriminatorId) - ?? await base.GetByDiscriminator(discriminatorId, token); + ?? await base.Get(discriminatorId, token); return result; } diff --git a/DD.Persistence/Repositories/IDataSchemeRepository.cs b/DD.Persistence/Repositories/IDataSchemeRepository.cs index ff6ef4b..c14a9cf 100644 --- a/DD.Persistence/Repositories/IDataSchemeRepository.cs +++ b/DD.Persistence/Repositories/IDataSchemeRepository.cs @@ -18,8 +18,8 @@ public interface IDataSchemeRepository /// /// Вычитать схему /// - /// Дискриминатор системы + /// Идентификатор схемы /// /// - Task GetByDiscriminator(Guid discriminatorId, CancellationToken token); + Task Get(Guid dataSchemeId, CancellationToken token); } \ No newline at end of file diff --git a/DD.Persistence/Services/TimestampedValuesService.cs b/DD.Persistence/Services/TimestampedValuesService.cs index d6d6bf4..81a8fd3 100644 --- a/DD.Persistence/Services/TimestampedValuesService.cs +++ b/DD.Persistence/Services/TimestampedValuesService.cs @@ -113,14 +113,14 @@ public class TimestampedValuesService : ITimestampedValuesService /// /// Преобразовать результат запроса в набор dto /// - /// + /// /// /// /// - private async Task> Materialize(Guid discriminatorId, IEnumerable> queryResult, CancellationToken token) + private async Task> Materialize(Guid dataSchemeId, IEnumerable> queryResult, CancellationToken token) { - var systemSpecification = await dataSchemeRepository.GetByDiscriminator(discriminatorId, token); - if (systemSpecification is null) + var dataScheme = await dataSchemeRepository.Get(dataSchemeId, token); + if (dataScheme is null) { return []; } @@ -132,7 +132,7 @@ public class TimestampedValuesService : ITimestampedValuesService Timestamp = entity.Item1.ToUniversalTime() }; - var identity = systemSpecification!.PropNames; + var identity = dataScheme!.PropNames; var indexedIdentity = identity .Select((value, index) => new { index, value }); dto.Values = indexedIdentity @@ -154,7 +154,7 @@ public class TimestampedValuesService : ITimestampedValuesService /// Некорректный набор наименований полей private async Task CreateSystemSpecificationIfNotExist(Guid discriminatorId, string[] fieldNames, CancellationToken token) { - var systemSpecification = await dataSchemeRepository.GetByDiscriminator(discriminatorId, token); + var systemSpecification = await dataSchemeRepository.Get(discriminatorId, token); if (systemSpecification is null) { systemSpecification = new DataSchemeDto() -- 2.45.2 From 95859f3d6001dfa57b451f8a88ef738591ae5dbe Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 22 Jan 2025 16:30:32 +0500 Subject: [PATCH 16/16] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BD=D0=B0?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DD.Persistence.App/appsettings.Tests.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DD.Persistence.App/appsettings.Tests.json b/DD.Persistence.App/appsettings.Tests.json index e8d3cc4..2c890bb 100644 --- a/DD.Persistence.App/appsettings.Tests.json +++ b/DD.Persistence.App/appsettings.Tests.json @@ -1,6 +1,6 @@ { "DbConnection": { - "Host": "localhost", + "Host": "postgres", "Port": 5432, "Database": "persistence", "Username": "postgres", -- 2.45.2