Правки по результатам ревью #1
This commit is contained in:
parent
d8497ce46a
commit
16a14e7e19
@ -14,9 +14,9 @@ namespace DD.Persistence.API.Controllers;
|
||||
[Route("api/[controller]")]
|
||||
public class DataSourceSystemController : ControllerBase
|
||||
{
|
||||
private readonly IRelatedDataRepository<DataSourceSystemDto> dataSourceSystemRepository;
|
||||
private readonly IDataSourceSystemRepository dataSourceSystemRepository;
|
||||
|
||||
public DataSourceSystemController(IRelatedDataRepository<DataSourceSystemDto> dataSourceSystemRepository)
|
||||
public DataSourceSystemController(IDataSourceSystemRepository dataSourceSystemRepository)
|
||||
{
|
||||
this.dataSourceSystemRepository = dataSourceSystemRepository;
|
||||
}
|
||||
|
@ -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;
|
||||
/// Хранение наборов данных с отметкой времени.
|
||||
/// </summary>
|
||||
[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
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<TimestampedValuesDto>), (int)HttpStatusCode.OK)]
|
||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> 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
|
||||
/// <param name="timestampBegin">Фильтр позднее даты</param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet("gtdate")]
|
||||
[ProducesResponseType(typeof(IEnumerable<TimestampedValuesDto>), (int)HttpStatusCode.OK)]
|
||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> GetGtDate([FromRoute] Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetGtDate(discriminatorId, timestampBegin, token);
|
||||
@ -75,6 +80,8 @@ public class TimestampedValuesController : ControllerBase
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet("first")]
|
||||
[ProducesResponseType(typeof(IEnumerable<TimestampedValuesDto>), (int)HttpStatusCode.OK)]
|
||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> GetFirst([FromRoute] Guid discriminatorId, int take, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetFirst(discriminatorId, take, token);
|
||||
@ -89,6 +96,8 @@ public class TimestampedValuesController : ControllerBase
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet("last")]
|
||||
[ProducesResponseType(typeof(IEnumerable<TimestampedValuesDto>), (int)HttpStatusCode.OK)]
|
||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> GetLast([FromRoute] Guid discriminatorId, int take, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetLast(discriminatorId, take, token);
|
||||
@ -105,6 +114,8 @@ public class TimestampedValuesController : ControllerBase
|
||||
/// <param name="approxPointsCount"></param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet("resampled")]
|
||||
[ProducesResponseType(typeof(IEnumerable<TimestampedValuesDto>), (int)HttpStatusCode.OK)]
|
||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> 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);
|
||||
|
@ -53,6 +53,7 @@ public static class DependencyInjection
|
||||
public static void AddServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IWitsDataService, WitsDataService>();
|
||||
services.AddTransient<ITimestampedValuesService, TimestampedValuesService>();
|
||||
}
|
||||
|
||||
#region Authentication
|
||||
|
@ -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; } = [];
|
||||
}
|
@ -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; }
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ public class PersistenceDbContext : DbContext
|
||||
{
|
||||
public DbSet<Setpoint> Setpoint => Set<Setpoint>();
|
||||
|
||||
public DbSet<ValuesIdentity> ValuesIdentities => Set<ValuesIdentity>();
|
||||
public DbSet<DataScheme> DataSchemes => Set<DataScheme>();
|
||||
|
||||
public DbSet<TimestampedValues> TimestampedValues => Set<TimestampedValues>();
|
||||
|
||||
@ -31,8 +31,8 @@ public class PersistenceDbContext : DbContext
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<ValuesIdentity>()
|
||||
.Property(e => e.Identity)
|
||||
modelBuilder.Entity<DataScheme>()
|
||||
.Property(e => e.PropNames)
|
||||
.HasJsonConversion();
|
||||
|
||||
modelBuilder.Entity<TimestampedValues>()
|
||||
|
@ -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<Guid> 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<string>() { "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<TimestampedValues>();
|
||||
dbContext.CleanupDbSet<ValuesIdentity>();
|
||||
dbContext.CleanupDbSet<DataScheme>();
|
||||
}
|
||||
}
|
||||
|
17
DD.Persistence.Models/DataSchemeDto.cs
Normal file
17
DD.Persistence.Models/DataSchemeDto.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace DD.Persistence.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Схема для набора данных
|
||||
/// </summary>
|
||||
public class DataSchemeDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Дискриминатор
|
||||
/// </summary>
|
||||
public Guid DiscriminatorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Наименования полей
|
||||
/// </summary>
|
||||
public string[] PropNames { get; set; } = [];
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
namespace DD.Persistence.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Набор идентификаторов для набора данных
|
||||
/// </summary>
|
||||
public class ValuesIdentityDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Дискриминатор системы
|
||||
/// </summary>
|
||||
public Guid DiscriminatorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Идентификаторы
|
||||
/// </summary>
|
||||
public string[] Identity { get; set; } = [];
|
||||
}
|
@ -41,10 +41,8 @@ public static class DependencyInjection
|
||||
services.AddTransient<ITimestampedValuesRepository, TimestampedValuesRepository>();
|
||||
services.AddTransient<ITechMessagesRepository, TechMessagesRepository>();
|
||||
services.AddTransient<IParameterRepository, ParameterRepository>();
|
||||
services.AddTransient<IRelatedDataRepository<DataSourceSystemDto>,
|
||||
RelatedDataCachedRepository<DataSourceSystemDto, DataSourceSystem>>();
|
||||
services.AddTransient<IRelatedDataRepository<ValuesIdentityDto>,
|
||||
RelatedDataCachedRepository<ValuesIdentityDto, ValuesIdentity>>();
|
||||
services.AddTransient<IDataSourceSystemRepository, DataSourceSystemCachedRepository>();
|
||||
services.AddTransient<IDataSchemeRepository, DataSchemeCachedRepository>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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<DataScheme> GetQueryReadOnly() => db.Set<DataScheme>();
|
||||
|
||||
public virtual async Task Add(DataSchemeDto dataSourceSystemDto, CancellationToken token)
|
||||
{
|
||||
var entity = dataSourceSystemDto.Adapt<DataScheme>();
|
||||
|
||||
await db.Set<DataScheme>().AddAsync(entity, token);
|
||||
await db.SaveChangesAsync(token);
|
||||
}
|
||||
|
||||
public virtual async Task<DataSchemeDto?> 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<DataSchemeDto>()).FirstOrDefault();
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
@ -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<DataSourceSystem> GetQueryReadOnly() => db.Set<DataSourceSystem>();
|
||||
|
||||
public virtual async Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token)
|
||||
{
|
||||
var entity = dataSourceSystemDto.Adapt<DataSourceSystem>();
|
||||
|
||||
await db.Set<DataSourceSystem>().AddAsync(entity, token);
|
||||
await db.SaveChangesAsync(token);
|
||||
}
|
||||
|
||||
public virtual async Task<IEnumerable<DataSourceSystemDto>> Get(CancellationToken token)
|
||||
{
|
||||
var query = GetQueryReadOnly();
|
||||
var entities = await query.ToArrayAsync(token);
|
||||
var dtos = entities.Select(e => e.Adapt<DataSourceSystemDto>());
|
||||
|
||||
return dtos;
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
using DD.Persistence.Repositories;
|
||||
using Mapster;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DD.Persistence.Repository.Repositories;
|
||||
public class RelatedDataRepository<TDto, TEntity> : IRelatedDataRepository<TDto>
|
||||
where TDto : class, new()
|
||||
where TEntity : class, new()
|
||||
{
|
||||
protected DbContext db;
|
||||
public RelatedDataRepository(DbContext db)
|
||||
{
|
||||
this.db = db;
|
||||
}
|
||||
protected virtual IQueryable<TEntity> GetQueryReadOnly() => db.Set<TEntity>();
|
||||
|
||||
public virtual async Task Add(TDto dataSourceSystemDto, CancellationToken token)
|
||||
{
|
||||
var entity = dataSourceSystemDto.Adapt<TEntity>();
|
||||
|
||||
await db.Set<TEntity>().AddAsync(entity, token);
|
||||
await db.SaveChangesAsync(token);
|
||||
}
|
||||
|
||||
public virtual async Task<IEnumerable<TDto>> Get(CancellationToken token)
|
||||
{
|
||||
var query = GetQueryReadOnly();
|
||||
var entities = await query.ToArrayAsync(token);
|
||||
var dtos = entities.Select(e => e.Adapt<TDto>());
|
||||
|
||||
return dtos;
|
||||
}
|
||||
}
|
@ -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<DataSourceSystemDto> sourceSystemRepository;
|
||||
private readonly IDataSourceSystemRepository sourceSystemRepository;
|
||||
private DbContext db;
|
||||
|
||||
public TechMessagesRepository(DbContext db, IRelatedDataRepository<DataSourceSystemDto> sourceSystemRepository)
|
||||
public TechMessagesRepository(DbContext db, IDataSourceSystemRepository sourceSystemRepository)
|
||||
{
|
||||
this.db = db;
|
||||
this.sourceSystemRepository = sourceSystemRepository;
|
||||
|
@ -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<ValuesIdentityDto> relatedDataRepository;
|
||||
|
||||
public TimestampedValuesRepository(DbContext db, IRelatedDataRepository<ValuesIdentityDto> relatedDataRepository)
|
||||
public TimestampedValuesRepository(DbContext db)
|
||||
{
|
||||
this.db = db;
|
||||
this.relatedDataRepository = relatedDataRepository;
|
||||
}
|
||||
|
||||
protected virtual IQueryable<TimestampedValues> GetQueryReadOnly() => this.db.Set<TimestampedValues>()
|
||||
.Include(e => e.ValuesIdentity);
|
||||
protected virtual IQueryable<TimestampedValues> GetQueryReadOnly() => this.db.Set<TimestampedValues>();
|
||||
|
||||
public async virtual Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
|
||||
{
|
||||
var timestampedValuesEntities = new List<TimestampedValues>();
|
||||
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<IEnumerable<TimestampedValuesDto>> Get(Guid discriminatorId, DateTimeOffset? timestampBegin, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
||||
public async virtual Task<IEnumerable<Tuple<DateTimeOffset, object[]>>> Get(Guid discriminatorId, DateTimeOffset? timestampBegin, IEnumerable<string>? 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<IEnumerable<TimestampedValuesDto>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token)
|
||||
public async virtual Task<IEnumerable<Tuple<DateTimeOffset, object[]>>> 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<IEnumerable<TimestampedValuesDto>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token)
|
||||
public async virtual Task<IEnumerable<Tuple<DateTimeOffset, object[]>>> 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<IEnumerable<TimestampedValuesDto>> GetResampledData(
|
||||
public async virtual Task<IEnumerable<Tuple<DateTimeOffset, object[]>>> 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<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
|
||||
public async virtual Task<IEnumerable<Tuple<DateTimeOffset, object[]>>> 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<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
|
||||
@ -156,37 +160,12 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
|
||||
return query.CountAsync(token);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<TimestampedValuesDto>> Materialize(Guid discriminatorId, IQueryable<TimestampedValues> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Применить фильтр по дате
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="timestampBegin"></param>
|
||||
/// <returns></returns>
|
||||
private IQueryable<TimestampedValues> ApplyGeTimestamp(IQueryable<TimestampedValues> query, DateTimeOffset timestampBegin)
|
||||
{
|
||||
var geTimestampUtc = timestampBegin.ToUniversalTime();
|
||||
@ -196,46 +175,4 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEnumerable<TimestampedValuesDto> ReduceSetColumnsByNames(IEnumerable<TimestampedValuesDto> dtos, IEnumerable<string> 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}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<DataSchemeDto?> GetByDiscriminator(Guid discriminatorId, CancellationToken token)
|
||||
{
|
||||
var result = memoryCache.Get<DataSchemeDto>(discriminatorId)
|
||||
?? await base.GetByDiscriminator(discriminatorId, token);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -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<TDto, TEntity> : RelatedDataRepository<TDto, TEntity>
|
||||
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<IEnumerable<TDto>> Get(CancellationToken token)
|
||||
public override async Task<IEnumerable<DataSourceSystemDto>> Get(CancellationToken token)
|
||||
{
|
||||
var systems = await memoryCache.GetOrCreateAsync(SystemCacheKey, async (cacheEntry) =>
|
||||
{
|
@ -12,7 +12,7 @@
|
||||
|
||||
// private const int CacheItemsCount = 3600;
|
||||
|
||||
// public TimestampedValuesCachedRepository(DbContext db, IRelatedDataRepository<ValuesIdentityDto> relatedDataRepository) : base(db, relatedDataRepository)
|
||||
// public TimestampedValuesCachedRepository(DbContext db, IDataSourceSystemRepository<ValuesIdentityDto> relatedDataRepository) : base(db, relatedDataRepository)
|
||||
// {
|
||||
// //Task.Run(async () =>
|
||||
// //{
|
||||
|
@ -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<Type, Dictionary<string, TypeAccessor>> TypePropSelectors { get; set; } = new();
|
||||
|
||||
private static MethodInfo GetExtOrderMethod(string methodName)
|
||||
=> typeof(System.Linq.Queryable)
|
||||
=> typeof(Queryable)
|
||||
.GetMethods()
|
||||
.Where(m => m.Name == methodName &&
|
||||
m.IsGenericMethodDefinition &&
|
@ -1,4 +1,4 @@
|
||||
namespace DD.Persistence.Repository.Extensions;
|
||||
namespace DD.Persistence.Extensions;
|
||||
|
||||
public static class IEnumerableExtensions
|
||||
{
|
25
DD.Persistence/Repositories/IDataSchemeRepository.cs
Normal file
25
DD.Persistence/Repositories/IDataSchemeRepository.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using DD.Persistence.Models;
|
||||
|
||||
namespace DD.Persistence.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Репозиторий для работы со схемами наборов данных
|
||||
/// </summary>
|
||||
public interface IDataSchemeRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Добавить схему
|
||||
/// </summary>
|
||||
/// <param name="dataSourceSystemDto"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task Add(DataSchemeDto dataSourceSystemDto, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Вычитать схему
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор системы</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<DataSchemeDto?> GetByDiscriminator(Guid discriminatorId, CancellationToken token);
|
||||
}
|
23
DD.Persistence/Repositories/IDataSourceSystemRepository.cs
Normal file
23
DD.Persistence/Repositories/IDataSourceSystemRepository.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using DD.Persistence.Models;
|
||||
|
||||
namespace DD.Persistence.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Репозиторий для работы с системами - источниками данных
|
||||
/// </summary>
|
||||
public interface IDataSourceSystemRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Добавить систему - источник данных
|
||||
/// </summary>
|
||||
/// <param name="dataSourceSystemDto"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
public Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список систем - источников данных
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Task<IEnumerable<DataSourceSystemDto>> Get(CancellationToken token);
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
namespace DD.Persistence.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс по работе с простой структурой данных, подразумевающей наличие связи с более сложной
|
||||
/// В контексте TechMessagesRepository это системы - источники данных
|
||||
/// В контексте TimestampedValuesRepository это идентификационные наборы (ключи для значений в соответствии с индексами в хранимых массивах)
|
||||
/// </summary>
|
||||
public interface IRelatedDataRepository<TDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Добавить данные
|
||||
/// </summary>
|
||||
/// <param name="dataSourceSystemDto"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
public Task Add(TDto dataSourceSystemDto, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список данных
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Task<IEnumerable<TDto>> Get(CancellationToken token);
|
||||
}
|
@ -4,14 +4,14 @@ using DD.Persistence.RepositoriesAbstractions;
|
||||
namespace DD.Persistence.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс по работе с временными данными
|
||||
/// Репозиторий для работы с временными данными
|
||||
/// </summary>
|
||||
public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBaseRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Добавление записей
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator"></param>
|
||||
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
@ -35,7 +35,7 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedValuesDto>> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
||||
Task<IEnumerable<Tuple<DateTimeOffset, object[]>>> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных с начала
|
||||
@ -44,7 +44,7 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase
|
||||
/// <param name="takeCount">Количество</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token);
|
||||
Task<IEnumerable<Tuple<DateTimeOffset, object[]>>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных с конца
|
||||
@ -53,5 +53,5 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase
|
||||
/// <param name="takeCount">Количество</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token);
|
||||
Task<IEnumerable<Tuple<DateTimeOffset, object[]>>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace DD.Persistence.RepositoriesAbstractions;
|
||||
/// <summary>
|
||||
/// Интерфейс по работе с данными
|
||||
/// </summary>
|
||||
public interface ISyncRepository
|
||||
public interface ISyncRepository // ToDo: исчерпывающая абстракция
|
||||
{
|
||||
/// <summary>
|
||||
/// Получить данные, начиная с определенной даты
|
||||
@ -15,7 +15,7 @@ public interface ISyncRepository
|
||||
/// <param name="dateBegin">дата начала</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token);
|
||||
Task<IEnumerable<Tuple<DateTimeOffset, object[]>>> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token);
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
@ -5,7 +5,7 @@ namespace DD.Persistence.RepositoriesAbstractions;
|
||||
/// <summary>
|
||||
/// Интерфейс по работе с прореженными данными
|
||||
/// </summary>
|
||||
public interface ITimeSeriesBaseRepository
|
||||
public interface ITimeSeriesBaseRepository // ToDo: исчерпывающая абстракция
|
||||
{
|
||||
/// <summary>
|
||||
/// Получить список объектов с прореживанием
|
||||
@ -16,7 +16,7 @@ public interface ITimeSeriesBaseRepository
|
||||
/// <param name="approxPointsCount"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetResampledData(
|
||||
Task<IEnumerable<Tuple<DateTimeOffset, object[]>>> GetResampledData(
|
||||
Guid discriminatorId,
|
||||
DateTimeOffset dateBegin,
|
||||
double intervalSec = 600d,
|
||||
|
@ -0,0 +1,85 @@
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Common;
|
||||
|
||||
namespace DD.Persistence.Services.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Сервис для работы с временными данными
|
||||
/// </summary>
|
||||
public interface ITimestampedValuesService
|
||||
{
|
||||
/// <summary>
|
||||
/// Добавление записей
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId"></param>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Количество записей по указанному набору в БД. Для пагинации
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> Count(Guid discriminatorId, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных с фильтрацией. Значение фильтра null - отключен
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="geTimestamp">Фильтр позднее даты</param>
|
||||
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
|
||||
/// <param name="skip"></param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedValuesDto>> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных с начала
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="takeCount">Количество</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные, начиная с определенной даты
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId"></param>
|
||||
/// <param name="timestampBegin">дата начала</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных с конца
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="takeCount">Количество</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список объектов с прореживанием
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId"></param>
|
||||
/// <param name="timestampBegin">дата начала</param>
|
||||
/// <param name="intervalSec"></param>
|
||||
/// <param name="approxPointsCount"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetResampledData(Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Получить диапазон дат, для которых есть данные
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token);
|
||||
}
|
201
DD.Persistence/Services/TimestampedValuesService.cs
Normal file
201
DD.Persistence/Services/TimestampedValuesService.cs
Normal file
@ -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;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public class TimestampedValuesService : ITimestampedValuesService
|
||||
{
|
||||
private readonly ITimestampedValuesRepository timestampedValuesRepository;
|
||||
private readonly IDataSchemeRepository dataSchemeRepository;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TimestampedValuesService(ITimestampedValuesRepository timestampedValuesRepository, IDataSchemeRepository relatedDataRepository)
|
||||
{
|
||||
this.timestampedValuesRepository = timestampedValuesRepository;
|
||||
this.dataSchemeRepository = relatedDataRepository;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<TimestampedValuesDto>> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable<string>? 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<TimestampedValuesDto>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetFirst(discriminatorId, takeCount, token);
|
||||
|
||||
var dtos = await Materialize(discriminatorId, result, token);
|
||||
|
||||
return dtos;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<TimestampedValuesDto>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetLast(discriminatorId, takeCount, token);
|
||||
|
||||
var dtos = await Materialize(discriminatorId, result, token);
|
||||
|
||||
return dtos;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<TimestampedValuesDto>> 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset beginTimestamp, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetGtDate(discriminatorId, beginTimestamp, token);
|
||||
|
||||
var dtos = await Materialize(discriminatorId, result, token);
|
||||
|
||||
return dtos;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<int> Count(Guid discriminatorId, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.Count(discriminatorId, token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async virtual Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetDatesRange(discriminatorId, token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразовать результат запроса в набор dto
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId"></param>
|
||||
/// <param name="queryResult"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<IEnumerable<TimestampedValuesDto>> Materialize(Guid discriminatorId, IEnumerable<Tuple<DateTimeOffset, object[]>> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создать спецификацию, при отсутствии таковой
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор системы</param>
|
||||
/// <param name="fieldNames">Набор наименований полей</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException">Некорректный набор наименований полей</exception>
|
||||
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}]");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Отсеить лишние поля в соответствии с заданным фильтром
|
||||
/// </summary>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="fieldNames">Поля, которые необходимо оставить</param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<TimestampedValuesDto> ReduceSetColumnsByNames(IEnumerable<TimestampedValuesDto> dtos, IEnumerable<string> fieldNames)
|
||||
{
|
||||
var result = dtos.Select(dto =>
|
||||
{
|
||||
var reducedValues = dto.Values
|
||||
.Where(v => fieldNames.Contains(v.Key))
|
||||
.ToDictionary();
|
||||
dto.Values = reducedValues;
|
||||
|
||||
return dto;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user