Compare commits
4 Commits
Timestampe
...
master
Author | SHA1 | Date | |
---|---|---|---|
3d6eb1a28c | |||
|
8596f5b35c | ||
|
f844656ed0 | ||
|
9a281238e9 |
@ -4,7 +4,6 @@ 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;
|
||||
|
||||
|
20
DD.Persistence.API/Controllers/DataSaubController.cs
Normal file
20
DD.Persistence.API/Controllers/DataSaubController.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Repositories;
|
||||
|
||||
namespace DD.Persistence.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Работа с временными данными
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/[controller]")]
|
||||
public class DataSaubController : TimeSeriesController<DataSaubDto>
|
||||
{
|
||||
public DataSaubController(ITimeSeriesDataRepository<DataSaubDto> timeSeriesDataRepository) : base(timeSeriesDataRepository)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ 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;
|
||||
|
||||
|
@ -4,7 +4,6 @@ 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;
|
||||
|
||||
|
76
DD.Persistence.API/Controllers/TimeSeriesController.cs
Normal file
76
DD.Persistence.API/Controllers/TimeSeriesController.cs
Normal file
@ -0,0 +1,76 @@
|
||||
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 TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDto>
|
||||
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||
{
|
||||
private readonly ITimeSeriesDataRepository<TDto> timeSeriesDataRepository;
|
||||
|
||||
public TimeSeriesController(ITimeSeriesDataRepository<TDto> timeSeriesDataRepository)
|
||||
{
|
||||
this.timeSeriesDataRepository = timeSeriesDataRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить список объектов, удовлетворяющий диапазону дат
|
||||
/// </summary>
|
||||
/// <param name="dateBegin"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> Get(DateTimeOffset dateBegin, CancellationToken token)
|
||||
{
|
||||
var result = await timeSeriesDataRepository.GetGtDate(dateBegin, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("datesRange")]
|
||||
public async Task<IActionResult> GetDatesRange(CancellationToken token)
|
||||
{
|
||||
var result = await timeSeriesDataRepository.GetDatesRange(token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить список объектов с прореживанием, удовлетворяющий диапазону дат
|
||||
/// </summary>
|
||||
/// <param name="dateBegin"></param>
|
||||
/// <param name="intervalSec"></param>
|
||||
/// <param name="approxPointsCount"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("resampled")]
|
||||
public async Task<IActionResult> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default)
|
||||
{
|
||||
var result = await timeSeriesDataRepository.GetResampledData(dateBegin, intervalSec, approxPointsCount, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавить записи
|
||||
/// </summary>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> AddRange(IEnumerable<TDto> dtos, CancellationToken token)
|
||||
{
|
||||
var result = await timeSeriesDataRepository.AddRange(dtos, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
|
||||
}
|
104
DD.Persistence.API/Controllers/TimestampedSetController.cs
Normal file
104
DD.Persistence.API/Controllers/TimestampedSetController.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Repositories;
|
||||
using System.Net;
|
||||
|
||||
namespace DD.Persistence.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Хранение наборов данных с отметкой времени.
|
||||
/// Не оптимизировано под большие данные.
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/[controller]/{idDiscriminator}")]
|
||||
public class TimestampedSetController : ControllerBase
|
||||
{
|
||||
private readonly ITimestampedSetRepository repository;
|
||||
|
||||
public TimestampedSetController(ITimestampedSetRepository repository)
|
||||
{
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Записать новые данные
|
||||
/// Предполагается что данные с одним дискриминатором имеют одинаковую структуру
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="sets"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>кол-во затронутых записей</returns>
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> AddRange([FromRoute] Guid idDiscriminator, [FromBody] IEnumerable<TimestampedSetDto> sets, CancellationToken token)
|
||||
{
|
||||
var result = await repository.AddRange(idDiscriminator, sets, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных с фильтрацией. Значение фильтра null - отключен
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="geTimestamp">Фильтр позднее даты</param>
|
||||
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
|
||||
/// <param name="skip"></param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>Фильтрованный набор данных с сортировкой по отметке времени</returns>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<TimestampedSetDto>), (int)HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, [FromQuery] IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
||||
{
|
||||
var result = await repository.Get(idDiscriminator, geTimestamp, columnNames, skip, take, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить последние данные
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>Фильтрованный набор данных с сортировкой по отметке времени</returns>
|
||||
[HttpGet("last")]
|
||||
[ProducesResponseType(typeof(IEnumerable<TimestampedSetDto>), (int)HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> GetLast(Guid idDiscriminator, [FromQuery] IEnumerable<string>? columnNames, int take, CancellationToken token)
|
||||
{
|
||||
var result = await repository.GetLast(idDiscriminator, columnNames, take, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Диапазон дат за которые есть данные
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>Дата первой и последней записи</returns>
|
||||
[HttpGet("datesRange")]
|
||||
[ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)]
|
||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||
public async Task<IActionResult> GetDatesRange(Guid idDiscriminator, CancellationToken token)
|
||||
{
|
||||
var result = await repository.GetDatesRange(idDiscriminator, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Количество записей по указанному набору в БД. Для пагинации.
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("count")]
|
||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||
public async Task<IActionResult> Count(Guid idDiscriminator, CancellationToken token)
|
||||
{
|
||||
var result = await repository.Count(idDiscriminator, token);
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Хранение наборов данных с отметкой времени.
|
||||
/// Не оптимизировано под большие данные.
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
//[Authorize]
|
||||
[Route("api/[controller]/{discriminatorId}")]
|
||||
public class TimestampedValuesController : ControllerBase
|
||||
{
|
||||
private readonly ITimestampedValuesRepository timestampedValuesRepository;
|
||||
|
||||
public TimestampedValuesController(ITimestampedValuesRepository repository)
|
||||
{
|
||||
this.timestampedValuesRepository = repository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Записать новые данные.
|
||||
/// Предполагается что данные с одним дискриминатором имеют одинаковую структуру
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
|
||||
public async Task<IActionResult> AddRange([FromRoute] Guid discriminatorId, [FromBody] IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.AddRange(discriminatorId, dtos, token);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных с фильтрацией. Значение фильтра null - отключен
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="timestampBegin">Фильтр позднее даты</param>
|
||||
/// <param name="columnNames">Фильтр свойств набора</param>
|
||||
/// <param name="skip"></param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet]
|
||||
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);
|
||||
|
||||
return result.Any() ? Ok(result) : NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные, начиная с заданной отметки времени
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="timestampBegin">Фильтр позднее даты</param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet("gtdate")]
|
||||
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> GetGtDate([FromRoute] Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetGtDate(discriminatorId, timestampBegin, token);
|
||||
|
||||
return result.Any() ? Ok(result) : NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные c начала
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet("first")]
|
||||
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> GetFirst([FromRoute] Guid discriminatorId, int take, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetFirst(discriminatorId, take, token);
|
||||
|
||||
return result.Any() ? Ok(result) : NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные c конца
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet("last")]
|
||||
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> GetLast([FromRoute] Guid discriminatorId, int take, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetLast(discriminatorId, take, token);
|
||||
|
||||
return result.Any() ? Ok(result) : NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить список объектов с прореживанием, удовлетворяющий диапазону дат
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="timestampBegin">Фильтр позднее даты</param>
|
||||
/// <param name="intervalSec"></param>
|
||||
/// <param name="approxPointsCount"></param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet("resampled")]
|
||||
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);
|
||||
|
||||
return result.Any() ? Ok(result) : NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить количество записей по указанному набору в БД. Для пагинации
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet("count")]
|
||||
public async Task<ActionResult<int>> Count([FromRoute] Guid discriminatorId, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.Count(discriminatorId, token);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить диапазон дат, в пределах которых хранятся даные
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId"></param>
|
||||
/// <param name="token"></param>
|
||||
[HttpGet("datesRange")]
|
||||
public async Task<ActionResult<DatesRangeDto>> GetDatesRange([FromRoute] Guid discriminatorId, CancellationToken token)
|
||||
{
|
||||
var result = await timestampedValuesRepository.GetDatesRange(discriminatorId, token);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ 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;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"DbConnection": {
|
||||
"Host": "localhost",
|
||||
"Host": "postgres",
|
||||
"Port": 5432,
|
||||
"Database": "persistence",
|
||||
"Username": "postgres",
|
||||
|
@ -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,
|
||||
|
@ -12,13 +12,13 @@ public abstract class BaseClient
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public async Task<T> ExecuteGetResponse<T>(Func<Task<IApiResponse<T>>> getMethod, CancellationToken token)
|
||||
public async Task<T?> ExecuteGetResponse<T>(Func<Task<IApiResponse<T>>> getMethod, CancellationToken token)
|
||||
{
|
||||
var response = await getMethod.Invoke().WaitAsync(token);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return response.Content!;
|
||||
return response.Content;
|
||||
}
|
||||
|
||||
var exception = response.GetPersistenceException();
|
||||
|
@ -4,7 +4,6 @@ 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
|
||||
|
@ -1,5 +1,4 @@
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Common;
|
||||
using DD.Persistence.Models.Requests;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces;
|
||||
|
@ -1,5 +1,4 @@
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Common;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Common;
|
||||
using DD.Persistence.Models.Requests;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces;
|
||||
|
@ -0,0 +1,44 @@
|
||||
using DD.Persistence.Models;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Клиент для работы с временными данными
|
||||
/// </summary>
|
||||
/// <typeparam name="TDto"></typeparam>
|
||||
public interface ITimeSeriesClient<TDto> : IDisposable where TDto : class, ITimeSeriesAbstractDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Добавление записей
|
||||
/// </summary>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> AddRange(IEnumerable<TDto> dtos, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список объектов, удовлетворяющий диапазону дат
|
||||
/// </summary>
|
||||
/// <param name="dateBegin"></param>
|
||||
/// <param name="dateEnd"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TDto>> Get(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<DatesRangeDto?> GetDatesRange(CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список объектов с прореживанием, удовлетворяющий диапазону дат
|
||||
/// </summary>
|
||||
/// <param name="dateBegin"></param>
|
||||
/// <param name="intervalSec"></param>
|
||||
/// <param name="approxPointsCount"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TDto>> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default);
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
using DD.Persistence.Models;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Клиент для работы с репозиторием для хранения разных наборов данных рядов.
|
||||
/// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest.
|
||||
/// idDiscriminator формируют клиенты и только им известно что они обозначают.
|
||||
/// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено.
|
||||
/// </summary>
|
||||
public interface ITimestampedSetClient : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Записать новые данные
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator"></param>
|
||||
/// <param name="sets"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> AddRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Количество записей по указанному набору в БД. Для пагинации
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> Count(Guid idDiscriminator, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных с фильтрацией. Значение фильтра null - отключен
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator"></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<TimestampedSetDto>> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Диапазон дат за которые есть данные
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="idDiscriminator"></param>
|
||||
/// <param name="columnNames"></param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<TimestampedSetDto>> GetLast(Guid idDiscriminator, IEnumerable<string>? columnNames, int take, CancellationToken token);
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Common;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Клиент для работы с репозиторием для хранения разных наборов данных рядов.
|
||||
/// discriminatorId - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest.
|
||||
/// discriminatorId формируют клиенты и только им известно что они обозначают.
|
||||
/// </summary>
|
||||
public interface ITimestampedValuesClient : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Записать новые данные
|
||||
/// Предполагается что данные с одним дискриминатором имеют одинаковую структуру
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные с фильтрацией. Значение фильтра null - отключен
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="timestampBegin">Фильтр позднее даты</param>
|
||||
/// <param name="columnNames">Фильтр свойств набора</param>
|
||||
/// <param name="skip"></param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
Task<IEnumerable<TimestampedValuesDto>> Get(Guid discriminatorId, DateTimeOffset? timestampBegin, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные, начиная с заданной отметки времени
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="timestampBegin">Фильтр позднее даты</param>
|
||||
/// <param name="token"></param>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные с начала
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetFirst(Guid discriminatorId, int take, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные с конца
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="take"></param>
|
||||
/// <param name="token"></param>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetLast(Guid discriminatorId, int take, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные с прореживанием, удовлетворяющем диапазону дат
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="timestampBegin"></param>
|
||||
/// <param name="intervalSec"></param>
|
||||
/// <param name="approxPointsCount"></param>
|
||||
/// <param name="token"></param>
|
||||
Task<IEnumerable<TimestampedValuesDto>> GetResampledData(Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Количество записей по указанному набору в БД. Для пагинации
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="token"></param>
|
||||
Task<int> Count(Guid discriminatorId, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Диапазон дат, в пределах которых осуществляется хранение данных
|
||||
/// </summary>
|
||||
/// <param name="discriminatorId">Дискриминатор (идентификатор) набора</param>
|
||||
/// <param name="token"></param>
|
||||
Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token);
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Common;
|
||||
using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces;
|
||||
|
@ -1,5 +1,4 @@
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Common;
|
||||
using DD.Persistence.Models.Requests;
|
||||
using Refit;
|
||||
|
||||
|
@ -5,10 +5,6 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
|
||||
/// <summary>
|
||||
/// Базовый Refit интерфейс
|
||||
/// </summary>
|
||||
public interface IRefitClient
|
||||
{
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Common;
|
||||
using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
|
@ -1,8 +1,6 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Requests;
|
||||
using Refit;
|
||||
using DD.Persistence.Models.Common;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit
|
||||
{
|
||||
|
@ -0,0 +1,21 @@
|
||||
using DD.Persistence.Models;
|
||||
using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
public interface IRefitTimeSeriesClient<TDto> : IRefitClient, IDisposable
|
||||
where TDto : class, ITimeSeriesAbstractDto
|
||||
{
|
||||
private const string BaseRoute = "/api/dataSaub";
|
||||
|
||||
[Post($"{BaseRoute}")]
|
||||
Task<IApiResponse<int>> AddRange(IEnumerable<TDto> dtos, CancellationToken token);
|
||||
|
||||
[Get($"{BaseRoute}")]
|
||||
Task<IApiResponse<IEnumerable<TDto>>> Get(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
|
||||
|
||||
[Get($"{BaseRoute}/resampled")]
|
||||
Task<IApiResponse<IEnumerable<TDto>>> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default);
|
||||
|
||||
[Get($"{BaseRoute}/datesRange")]
|
||||
Task<IApiResponse<DatesRangeDto?>> GetDatesRange(CancellationToken token);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
using DD.Persistence.Models;
|
||||
using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
|
||||
public interface IRefitTimestampedSetClient : IRefitClient, IDisposable
|
||||
{
|
||||
private const string baseUrl = "/api/TimestampedSet/{idDiscriminator}";
|
||||
|
||||
[Post(baseUrl)]
|
||||
Task<IApiResponse<int>> AddRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token);
|
||||
|
||||
[Get(baseUrl)]
|
||||
Task<IApiResponse<IEnumerable<TimestampedSetDto>>> Get(Guid idDiscriminator, [Query] DateTimeOffset? geTimestamp, [Query] IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
||||
|
||||
[Get($"{baseUrl}/last")]
|
||||
Task<IApiResponse<IEnumerable<TimestampedSetDto>>> GetLast(Guid idDiscriminator, [Query] IEnumerable<string>? columnNames, int take, CancellationToken token);
|
||||
|
||||
[Get($"{baseUrl}/count")]
|
||||
Task<IApiResponse<int>> Count(Guid idDiscriminator, CancellationToken token);
|
||||
|
||||
[Get($"{baseUrl}/datesRange")]
|
||||
Task<IApiResponse<DatesRangeDto?>> GetDatesRange(Guid idDiscriminator, CancellationToken token);
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Common;
|
||||
using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
|
||||
/// <summary>
|
||||
/// Refit интерфейс для TimestampedValuesController
|
||||
/// </summary>
|
||||
public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable
|
||||
{
|
||||
private const string baseUrl = "/api/TimestampedValues/{discriminatorId}";
|
||||
|
||||
/// <summary>
|
||||
/// Записать новые данные
|
||||
/// </summary>
|
||||
[Post(baseUrl)]
|
||||
Task<IApiResponse<int>> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных с фильтрацией
|
||||
/// </summary>
|
||||
[Get(baseUrl)]
|
||||
Task<IApiResponse<IEnumerable<TimestampedValuesDto>>> Get(Guid discriminatorId, DateTimeOffset? timestampBegin, [Query(CollectionFormat.Multi)] IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные, начиная с заданной отметки времени
|
||||
/// </summary>
|
||||
[Get($"{baseUrl}/gtdate")]
|
||||
Task<IApiResponse<IEnumerable<TimestampedValuesDto>>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные c начала
|
||||
/// </summary>
|
||||
[Get($"{baseUrl}/first")]
|
||||
Task<IApiResponse<IEnumerable<TimestampedValuesDto>>> GetFirst(Guid discriminatorId, int take, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить данные c конца
|
||||
/// </summary>
|
||||
[Get($"{baseUrl}/last")]
|
||||
Task<IApiResponse<IEnumerable<TimestampedValuesDto>>> GetLast(Guid discriminatorId, int take, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список объектов с прореживанием, удовлетворяющий диапазону временных отметок
|
||||
/// </summary>
|
||||
[Get($"{baseUrl}/resampled")]
|
||||
Task<IApiResponse<IEnumerable<TimestampedValuesDto>>> GetResampledData(Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Получить количество записей по указанному набору в БД. Для пагинации
|
||||
/// </summary>
|
||||
[Get($"{baseUrl}/count")]
|
||||
Task<IApiResponse<int>> Count(Guid discriminatorId, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить диапазон дат, в пределах которых хранятся даные
|
||||
/// </summary>
|
||||
[Get($"{baseUrl}/datesRange")]
|
||||
Task<IApiResponse<DatesRangeDto?>> GetDatesRange(Guid discriminatorId, CancellationToken token);
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models;
|
||||
using Refit;
|
||||
using DD.Persistence.Models.Common;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
public interface IRefitWitsDataClient : IRefitClient, IDisposable
|
||||
|
@ -3,7 +3,6 @@ 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;
|
||||
|
||||
|
@ -4,7 +4,6 @@ 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;
|
||||
|
||||
|
55
DD.Persistence.Client/Clients/TimeSeriesClient.cs
Normal file
55
DD.Persistence.Client/Clients/TimeSeriesClient.cs
Normal file
@ -0,0 +1,55 @@
|
||||
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;
|
||||
|
||||
namespace DD.Persistence.Client.Clients;
|
||||
public class TimeSeriesClient<TDto> : BaseClient, ITimeSeriesClient<TDto> where TDto : class, ITimeSeriesAbstractDto
|
||||
{
|
||||
private readonly IRefitTimeSeriesClient<TDto> timeSeriesClient;
|
||||
|
||||
public TimeSeriesClient(IRefitClientFactory<IRefitTimeSeriesClient<TDto>> refitTechMessagesClientFactory, ILogger<TimeSeriesClient<TDto>> logger) : base(logger)
|
||||
{
|
||||
this.timeSeriesClient = refitTechMessagesClientFactory.Create();
|
||||
}
|
||||
|
||||
public async Task<int> AddRange(IEnumerable<TDto> dtos, CancellationToken token)
|
||||
{
|
||||
var result = await ExecutePostResponse(
|
||||
async () => await timeSeriesClient.AddRange(dtos, token), token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TDto>> Get(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
|
||||
{
|
||||
var result = await ExecuteGetResponse(
|
||||
async () => await timeSeriesClient.Get(dateBegin, dateEnd, token), token);
|
||||
|
||||
return result!;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TDto>> 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<DatesRangeDto?> GetDatesRange(CancellationToken token)
|
||||
{
|
||||
var result = await ExecuteGetResponse(
|
||||
async () => await timeSeriesClient.GetDatesRange(token), token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
timeSeriesClient.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
63
DD.Persistence.Client/Clients/TimestampedSetClient.cs
Normal file
63
DD.Persistence.Client/Clients/TimestampedSetClient.cs
Normal file
@ -0,0 +1,63 @@
|
||||
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;
|
||||
|
||||
namespace DD.Persistence.Client.Clients;
|
||||
public class TimestampedSetClient : BaseClient, ITimestampedSetClient
|
||||
{
|
||||
private readonly IRefitTimestampedSetClient refitTimestampedSetClient;
|
||||
|
||||
public TimestampedSetClient(IRefitClientFactory<IRefitTimestampedSetClient> refitTimestampedSetClientFactory, ILogger<TimestampedSetClient> logger) : base(logger)
|
||||
{
|
||||
this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create();
|
||||
}
|
||||
|
||||
public async Task<int> AddRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token)
|
||||
{
|
||||
var result = await ExecutePostResponse(
|
||||
async () => await refitTimestampedSetClient.AddRange(idDiscriminator, sets, token), token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TimestampedSetDto>> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable<string>? 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<IEnumerable<TimestampedSetDto>> GetLast(Guid idDiscriminator, IEnumerable<string>? columnNames, int take, CancellationToken token)
|
||||
{
|
||||
var result = await ExecuteGetResponse(
|
||||
async () => await refitTimestampedSetClient.GetLast(idDiscriminator, columnNames, take, token), token);
|
||||
|
||||
return result!;
|
||||
}
|
||||
|
||||
public async Task<int> Count(Guid idDiscriminator, CancellationToken token)
|
||||
{
|
||||
var result = await ExecuteGetResponse(
|
||||
async () => await refitTimestampedSetClient.Count(idDiscriminator, token), token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<DatesRangeDto?> 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);
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
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;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient
|
||||
{
|
||||
private readonly IRefitTimestampedValuesClient refitTimestampedSetClient;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TimestampedValuesClient(IRefitClientFactory<IRefitTimestampedValuesClient> refitTimestampedSetClientFactory, ILogger<TimestampedValuesClient> logger) : base(logger)
|
||||
{
|
||||
this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> sets, CancellationToken token)
|
||||
{
|
||||
var result = await ExecutePostResponse(
|
||||
async () => await refitTimestampedSetClient.AddRange(discriminatorId, sets, token), 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 ExecuteGetResponse(
|
||||
async () => await refitTimestampedSetClient.Get(discriminatorId, geTimestamp, columnNames, skip, take, token), token);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
|
||||
{
|
||||
var result = await ExecuteGetResponse(
|
||||
async () => await refitTimestampedSetClient.GetGtDate(discriminatorId, timestampBegin, token), token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<TimestampedValuesDto>> GetFirst(Guid discriminatorId, int take, CancellationToken token)
|
||||
{
|
||||
var result = await ExecuteGetResponse(
|
||||
async () => await refitTimestampedSetClient.GetFirst(discriminatorId, take, token), token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<TimestampedValuesDto>> GetLast(Guid discriminatorId, int take, CancellationToken token)
|
||||
{
|
||||
var result = await ExecuteGetResponse(
|
||||
async () => await refitTimestampedSetClient.GetLast(discriminatorId, take, token), token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<TimestampedValuesDto>> 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<int> Count(Guid discriminatorId, CancellationToken token)
|
||||
{
|
||||
var result = await ExecuteGetResponse(
|
||||
async () => await refitTimestampedSetClient.Count(discriminatorId, token), token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
|
||||
{
|
||||
var result = await ExecuteGetResponse(
|
||||
async () => await refitTimestampedSetClient.GetDatesRange(discriminatorId, token), token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
refitTimestampedSetClient.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ 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
|
||||
|
@ -9,13 +9,13 @@
|
||||
<!--Генерация NuGet пакета при сборке-->
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<!--Наименование-->
|
||||
<Title>Persistence.Client</Title>
|
||||
<Title>DD.Persistence.Client</Title>
|
||||
<!--Версия пакета-->
|
||||
<VersionPrefix>1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))</VersionPrefix>
|
||||
<!--Версия сборки-->
|
||||
<AssemblyVersion>1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))</AssemblyVersion>
|
||||
<!--Id пакета-->
|
||||
<PackageId>Persistence.Client</PackageId>
|
||||
<PackageId>DD.Persistence.Client</PackageId>
|
||||
|
||||
<!--Автор-->
|
||||
<Authors>Digital Drilling</Authors>
|
||||
@ -33,7 +33,7 @@
|
||||
<!--Формат пакета с символами-->
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<!--Путь к пакету-->
|
||||
<PackageOutputPath>C:\Projects\Nuget</PackageOutputPath>
|
||||
<PackageOutputPath>C:\Projects\Nuget\Persistence\Client</PackageOutputPath>
|
||||
|
||||
<!--Readme-->
|
||||
<PackageReadmeFile>Readme.md</PackageReadmeFile>
|
||||
@ -58,7 +58,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DD.Persistence\DD.Persistence.csproj" />
|
||||
<ProjectReference Include="..\DD.Persistence.Models\DD.Persistence.Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -22,7 +22,8 @@ public static class DependencyInjection
|
||||
services.AddTransient<IDataSourceSystemClient, DataSourceSystemClient>();
|
||||
services.AddTransient<ISetpointClient, SetpointClient>();
|
||||
services.AddTransient<ITechMessagesClient, TechMessagesClient>();
|
||||
services.AddTransient<ITimestampedValuesClient, TimestampedValuesClient>();
|
||||
services.AddTransient<ITimeSeriesClient<DataSaubDto>, TimeSeriesClient<DataSaubDto>>();
|
||||
services.AddTransient<ITimestampedSetClient, TimestampedSetClient>();
|
||||
services.AddTransient<IWitsDataClient, WitsDataClient>();
|
||||
return services;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace DD.Persistence.Database.Postgres.Migrations
|
||||
{
|
||||
[DbContext(typeof(PersistencePostgresContext))]
|
||||
[Migration("20250116093615_Init")]
|
||||
[Migration("20241220062251_Init")]
|
||||
partial class Init
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@ -20,7 +20,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.0")
|
||||
.HasAnnotation("ProductVersion", "8.0.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
@ -105,41 +105,27 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
b.ToTable("TechMessage");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b =>
|
||||
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedSet", b =>
|
||||
{
|
||||
b.Property<Guid>("DiscriminatorId")
|
||||
b.Property<Guid>("IdDiscriminator")
|
||||
.HasColumnType("uuid")
|
||||
.HasComment("Дискриминатор системы");
|
||||
.HasComment("Дискриминатор ссылка на тип сохраняемых данных");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("Временная отметка");
|
||||
.HasComment("Отметка времени, строго в UTC");
|
||||
|
||||
b.Property<string>("Values")
|
||||
b.Property<string>("Set")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasComment("Данные");
|
||||
.HasComment("Набор сохраняемых данных");
|
||||
|
||||
b.HasKey("DiscriminatorId", "Timestamp");
|
||||
b.HasKey("IdDiscriminator", "Timestamp");
|
||||
|
||||
b.ToTable("TimestampedValues");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DD.Persistence.Database.Entity.ValuesIdentity", b =>
|
||||
{
|
||||
b.Property<Guid>("DiscriminatorId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasComment("Дискриминатор системы");
|
||||
|
||||
b.Property<string[]>("Identity")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasComment("Идентификаторы");
|
||||
|
||||
b.HasKey("DiscriminatorId");
|
||||
|
||||
b.ToTable("ValuesIdentities");
|
||||
b.ToTable("TimestampedSets", t =>
|
||||
{
|
||||
t.HasComment("Общая таблица данных временных рядов");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b =>
|
||||
@ -195,13 +181,96 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
b.ToTable("ChangeLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DD.Persistence.Database.Model.DataSaub", b =>
|
||||
{
|
||||
b.Property<DateTimeOffset>("Date")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("date");
|
||||
|
||||
b.Property<double?>("AxialLoad")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("axialLoad");
|
||||
|
||||
b.Property<double?>("BitDepth")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("bitDepth");
|
||||
|
||||
b.Property<double?>("BlockPosition")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("blockPosition");
|
||||
|
||||
b.Property<double?>("BlockSpeed")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("blockSpeed");
|
||||
|
||||
b.Property<double?>("Flow")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("flow");
|
||||
|
||||
b.Property<double?>("HookWeight")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("hookWeight");
|
||||
|
||||
b.Property<int>("IdFeedRegulator")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("idFeedRegulator");
|
||||
|
||||
b.Property<int?>("Mode")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("mode");
|
||||
|
||||
b.Property<double?>("Mse")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("mse");
|
||||
|
||||
b.Property<short>("MseState")
|
||||
.HasColumnType("smallint")
|
||||
.HasColumnName("mseState");
|
||||
|
||||
b.Property<double?>("Pressure")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("pressure");
|
||||
|
||||
b.Property<double?>("Pump0Flow")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("pump0Flow");
|
||||
|
||||
b.Property<double?>("Pump1Flow")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("pump1Flow");
|
||||
|
||||
b.Property<double?>("Pump2Flow")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("pump2Flow");
|
||||
|
||||
b.Property<double?>("RotorSpeed")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("rotorSpeed");
|
||||
|
||||
b.Property<double?>("RotorTorque")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("rotorTorque");
|
||||
|
||||
b.Property<string>("User")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("user");
|
||||
|
||||
b.Property<double?>("WellDepth")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("wellDepth");
|
||||
|
||||
b.HasKey("Date");
|
||||
|
||||
b.ToTable("DataSaub");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b =>
|
||||
{
|
||||
b.Property<Guid>("Key")
|
||||
.HasColumnType("uuid")
|
||||
.HasComment("Ключ");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp")
|
||||
b.Property<DateTimeOffset>("Created")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("Дата создания уставки");
|
||||
|
||||
@ -214,7 +283,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
.HasColumnType("jsonb")
|
||||
.HasComment("Значение уставки");
|
||||
|
||||
b.HasKey("Key", "Timestamp");
|
||||
b.HasKey("Key", "Created");
|
||||
|
||||
b.ToTable("Setpoint");
|
||||
});
|
||||
@ -229,17 +298,6 @@ 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
|
||||
}
|
||||
}
|
@ -32,6 +32,35 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
table.PrimaryKey("PK_ChangeLog", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DataSaub",
|
||||
columns: table => new
|
||||
{
|
||||
date = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
mode = table.Column<int>(type: "integer", nullable: true),
|
||||
user = table.Column<string>(type: "text", nullable: true),
|
||||
wellDepth = table.Column<double>(type: "double precision", nullable: true),
|
||||
bitDepth = table.Column<double>(type: "double precision", nullable: true),
|
||||
blockPosition = table.Column<double>(type: "double precision", nullable: true),
|
||||
blockSpeed = table.Column<double>(type: "double precision", nullable: true),
|
||||
pressure = table.Column<double>(type: "double precision", nullable: true),
|
||||
axialLoad = table.Column<double>(type: "double precision", nullable: true),
|
||||
hookWeight = table.Column<double>(type: "double precision", nullable: true),
|
||||
rotorTorque = table.Column<double>(type: "double precision", nullable: true),
|
||||
rotorSpeed = table.Column<double>(type: "double precision", nullable: true),
|
||||
flow = table.Column<double>(type: "double precision", nullable: true),
|
||||
mseState = table.Column<short>(type: "smallint", nullable: false),
|
||||
idFeedRegulator = table.Column<int>(type: "integer", nullable: false),
|
||||
mse = table.Column<double>(type: "double precision", nullable: true),
|
||||
pump0Flow = table.Column<double>(type: "double precision", nullable: true),
|
||||
pump1Flow = table.Column<double>(type: "double precision", nullable: true),
|
||||
pump2Flow = table.Column<double>(type: "double precision", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DataSaub", x => x.date);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DataSourceSystem",
|
||||
columns: table => new
|
||||
@ -64,26 +93,28 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
columns: table => new
|
||||
{
|
||||
Key = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ"),
|
||||
Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания уставки"),
|
||||
Created = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания уставки"),
|
||||
Value = table.Column<object>(type: "jsonb", nullable: false, comment: "Значение уставки"),
|
||||
IdUser = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id автора последнего изменения")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Setpoint", x => new { x.Key, x.Timestamp });
|
||||
table.PrimaryKey("PK_Setpoint", x => new { x.Key, x.Created });
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ValuesIdentities",
|
||||
name: "TimestampedSets",
|
||||
columns: table => new
|
||||
{
|
||||
DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор системы"),
|
||||
Identity = table.Column<string[]>(type: "jsonb", nullable: false, comment: "Идентификаторы")
|
||||
IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор ссылка на тип сохраняемых данных"),
|
||||
Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Отметка времени, строго в UTC"),
|
||||
Set = table.Column<string>(type: "jsonb", nullable: false, comment: "Набор сохраняемых данных")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ValuesIdentities", x => x.DiscriminatorId);
|
||||
});
|
||||
table.PrimaryKey("PK_TimestampedSets", x => new { x.IdDiscriminator, x.Timestamp });
|
||||
},
|
||||
comment: "Общая таблица данных временных рядов");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TechMessage",
|
||||
@ -107,25 +138,6 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TimestampedValues",
|
||||
columns: table => new
|
||||
{
|
||||
Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Временная отметка"),
|
||||
DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор системы"),
|
||||
Values = table.Column<string>(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",
|
||||
@ -138,6 +150,9 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
migrationBuilder.DropTable(
|
||||
name: "ChangeLog");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "DataSaub");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ParameterData");
|
||||
|
||||
@ -148,13 +163,10 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
name: "TechMessage");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "TimestampedValues");
|
||||
name: "TimestampedSets");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "DataSourceSystem");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ValuesIdentities");
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.0")
|
||||
.HasAnnotation("ProductVersion", "8.0.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
@ -102,41 +102,27 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
b.ToTable("TechMessage");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b =>
|
||||
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedSet", b =>
|
||||
{
|
||||
b.Property<Guid>("DiscriminatorId")
|
||||
b.Property<Guid>("IdDiscriminator")
|
||||
.HasColumnType("uuid")
|
||||
.HasComment("Дискриминатор системы");
|
||||
.HasComment("Дискриминатор ссылка на тип сохраняемых данных");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("Временная отметка");
|
||||
.HasComment("Отметка времени, строго в UTC");
|
||||
|
||||
b.Property<string>("Values")
|
||||
b.Property<string>("Set")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasComment("Данные");
|
||||
.HasComment("Набор сохраняемых данных");
|
||||
|
||||
b.HasKey("DiscriminatorId", "Timestamp");
|
||||
b.HasKey("IdDiscriminator", "Timestamp");
|
||||
|
||||
b.ToTable("TimestampedValues");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DD.Persistence.Database.Entity.ValuesIdentity", b =>
|
||||
{
|
||||
b.Property<Guid>("DiscriminatorId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasComment("Дискриминатор системы");
|
||||
|
||||
b.Property<string[]>("Identity")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasComment("Идентификаторы");
|
||||
|
||||
b.HasKey("DiscriminatorId");
|
||||
|
||||
b.ToTable("ValuesIdentities");
|
||||
b.ToTable("TimestampedSets", t =>
|
||||
{
|
||||
t.HasComment("Общая таблица данных временных рядов");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b =>
|
||||
@ -192,13 +178,96 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
b.ToTable("ChangeLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DD.Persistence.Database.Model.DataSaub", b =>
|
||||
{
|
||||
b.Property<DateTimeOffset>("Date")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("date");
|
||||
|
||||
b.Property<double?>("AxialLoad")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("axialLoad");
|
||||
|
||||
b.Property<double?>("BitDepth")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("bitDepth");
|
||||
|
||||
b.Property<double?>("BlockPosition")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("blockPosition");
|
||||
|
||||
b.Property<double?>("BlockSpeed")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("blockSpeed");
|
||||
|
||||
b.Property<double?>("Flow")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("flow");
|
||||
|
||||
b.Property<double?>("HookWeight")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("hookWeight");
|
||||
|
||||
b.Property<int>("IdFeedRegulator")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("idFeedRegulator");
|
||||
|
||||
b.Property<int?>("Mode")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("mode");
|
||||
|
||||
b.Property<double?>("Mse")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("mse");
|
||||
|
||||
b.Property<short>("MseState")
|
||||
.HasColumnType("smallint")
|
||||
.HasColumnName("mseState");
|
||||
|
||||
b.Property<double?>("Pressure")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("pressure");
|
||||
|
||||
b.Property<double?>("Pump0Flow")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("pump0Flow");
|
||||
|
||||
b.Property<double?>("Pump1Flow")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("pump1Flow");
|
||||
|
||||
b.Property<double?>("Pump2Flow")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("pump2Flow");
|
||||
|
||||
b.Property<double?>("RotorSpeed")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("rotorSpeed");
|
||||
|
||||
b.Property<double?>("RotorTorque")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("rotorTorque");
|
||||
|
||||
b.Property<string>("User")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("user");
|
||||
|
||||
b.Property<double?>("WellDepth")
|
||||
.HasColumnType("double precision")
|
||||
.HasColumnName("wellDepth");
|
||||
|
||||
b.HasKey("Date");
|
||||
|
||||
b.ToTable("DataSaub");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b =>
|
||||
{
|
||||
b.Property<Guid>("Key")
|
||||
.HasColumnType("uuid")
|
||||
.HasComment("Ключ");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp")
|
||||
b.Property<DateTimeOffset>("Created")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("Дата создания уставки");
|
||||
|
||||
@ -211,7 +280,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
||||
.HasColumnType("jsonb")
|
||||
.HasComment("Значение уставки");
|
||||
|
||||
b.HasKey("Key", "Timestamp");
|
||||
b.HasKey("Key", "Created");
|
||||
|
||||
b.ToTable("Setpoint");
|
||||
});
|
||||
@ -226,17 +295,6 @@ 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
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
|
||||
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;
|
||||
|
||||
|
63
DD.Persistence.Database/Entity/DataSaub.cs
Normal file
63
DD.Persistence.Database/Entity/DataSaub.cs
Normal file
@ -0,0 +1,63 @@
|
||||
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; }
|
||||
}
|
@ -9,7 +9,7 @@ public class DataSourceSystem
|
||||
public Guid SystemId { get; set; }
|
||||
|
||||
[Required, Column(TypeName = "varchar(256)"), Comment("Наименование системы - источника данных")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public required string Name { get; set; }
|
||||
|
||||
[Comment("Описание системы - источника данных")]
|
||||
public string? Description { get; set; }
|
||||
|
@ -1,4 +1,5 @@
|
||||
namespace DD.Persistence.Database.EntityAbstractions;
|
||||
|
||||
namespace DD.Persistence.Database.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Часть записи, описывающая изменение
|
8
DD.Persistence.Database/Entity/ITimestampedData.cs
Normal file
8
DD.Persistence.Database/Entity/ITimestampedData.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace DD.Persistence.Database.Model;
|
||||
public interface ITimestampedData
|
||||
{
|
||||
/// <summary>
|
||||
/// Дата (должна быть обязательно в UTC)
|
||||
/// </summary>
|
||||
DateTimeOffset Date { get; set; }
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
using DD.Persistence.Database.EntityAbstractions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
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 : ITimestampedItem
|
||||
public class ParameterData
|
||||
{
|
||||
[Required, Comment("Дискриминатор системы")]
|
||||
public Guid DiscriminatorId { get; set; }
|
||||
|
@ -1,11 +1,10 @@
|
||||
using DD.Persistence.Database.EntityAbstractions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace DD.Persistence.Database.Model
|
||||
{
|
||||
[PrimaryKey(nameof(Key), nameof(Timestamp))]
|
||||
public class Setpoint : ITimestampedItem
|
||||
[PrimaryKey(nameof(Key), nameof(Created))]
|
||||
public class Setpoint
|
||||
{
|
||||
[Comment("Ключ")]
|
||||
public Guid Key { get; set; }
|
||||
@ -14,7 +13,7 @@ namespace DD.Persistence.Database.Model
|
||||
public required object Value { get; set; }
|
||||
|
||||
[Comment("Дата создания уставки")]
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
public DateTimeOffset Created { get; set; }
|
||||
|
||||
[Comment("Id автора последнего изменения")]
|
||||
public Guid IdUser { get; set; }
|
||||
|
@ -1,11 +1,10 @@
|
||||
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 : ITimestampedItem
|
||||
public class TechMessage
|
||||
{
|
||||
[Key, Comment("Id события")]
|
||||
public Guid EventId { get; set; }
|
||||
|
11
DD.Persistence.Database/Entity/TimestampedSet.cs
Normal file
11
DD.Persistence.Database/Entity/TimestampedSet.cs
Normal file
@ -0,0 +1,11 @@
|
||||
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<string, object> Set);
|
@ -1,22 +0,0 @@
|
||||
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(Timestamp))]
|
||||
public class TimestampedValues : ITimestampedItem
|
||||
{
|
||||
[Comment("Временная отметка"), Key]
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
|
||||
[Comment("Дискриминатор системы"),]
|
||||
public Guid DiscriminatorId { get; set; }
|
||||
|
||||
[Comment("Данные"), Column(TypeName = "jsonb")]
|
||||
public required object[] Values { get; set; }
|
||||
|
||||
[Required, ForeignKey(nameof(DiscriminatorId)), Comment("Идентификаторы")]
|
||||
public virtual ValuesIdentity? ValuesIdentity { get; set; }
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace DD.Persistence.Database.EntityAbstractions;
|
||||
public interface ITimestampedItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Дата (должна быть обязательно в UTC)
|
||||
/// </summary>
|
||||
DateTimeOffset Timestamp { get; set; }
|
||||
}
|
@ -9,11 +9,11 @@ namespace DD.Persistence.Database;
|
||||
/// </summary>
|
||||
public class PersistenceDbContext : DbContext
|
||||
{
|
||||
public DbSet<DataSaub> DataSaub => Set<DataSaub>();
|
||||
|
||||
public DbSet<Setpoint> Setpoint => Set<Setpoint>();
|
||||
|
||||
public DbSet<ValuesIdentity> ValuesIdentities => Set<ValuesIdentity>();
|
||||
|
||||
public DbSet<TimestampedValues> TimestampedValues => Set<TimestampedValues>();
|
||||
public DbSet<TimestampedSet> TimestampedSets => Set<TimestampedSet>();
|
||||
|
||||
public DbSet<ChangeLog> ChangeLog => Set<ChangeLog>();
|
||||
|
||||
@ -31,12 +31,8 @@ public class PersistenceDbContext : DbContext
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<ValuesIdentity>()
|
||||
.Property(e => e.Identity)
|
||||
.HasJsonConversion();
|
||||
|
||||
modelBuilder.Entity<TimestampedValues>()
|
||||
.Property(e => e.Values)
|
||||
modelBuilder.Entity<TimestampedSet>()
|
||||
.Property(e => e.Set)
|
||||
.HasJsonConversion();
|
||||
|
||||
modelBuilder.Entity<ChangeLog>()
|
||||
|
@ -0,0 +1,85 @@
|
||||
using DD.Persistence.Database.Model;
|
||||
using DD.Persistence.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace DD.Persistence.IntegrationTests.Controllers;
|
||||
public class DataSaubControllerTest : TimeSeriesBaseControllerTest<DataSaub, DataSaubDto>
|
||||
{
|
||||
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,
|
||||
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,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ namespace DD.Persistence.IntegrationTests.Controllers
|
||||
{
|
||||
public class TechMessagesControllerTest : BaseIntegrationTest
|
||||
{
|
||||
private static readonly string SystemCacheKey = $"{typeof(DataSourceSystem).FullName}CacheKey";
|
||||
private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DataSourceSystem).FullName}CacheKey";
|
||||
private readonly ITechMessagesClient techMessagesClient;
|
||||
private readonly IMemoryCache memoryCache;
|
||||
public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||
|
@ -0,0 +1,122 @@
|
||||
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 Mapster;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace DD.Persistence.IntegrationTests.Controllers;
|
||||
|
||||
public abstract class TimeSeriesBaseControllerTest<TEntity, TDto> : BaseIntegrationTest
|
||||
where TEntity : class, ITimestampedData, new()
|
||||
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||
{
|
||||
private readonly ITimeSeriesClient<TDto> timeSeriesClient;
|
||||
|
||||
public TimeSeriesBaseControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||
{
|
||||
dbContext.CleanupDbSet<TEntity>();
|
||||
|
||||
var refitClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<IRefitClientFactory<IRefitTimeSeriesClient<TDto>>>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TimeSeriesClient<TDto>>>();
|
||||
|
||||
timeSeriesClient = scope.ServiceProvider
|
||||
.GetRequiredService<ITimeSeriesClient<TDto>>();
|
||||
}
|
||||
|
||||
public async Task InsertRangeSuccess(TDto dto)
|
||||
{
|
||||
//arrange
|
||||
var expected = dto.Adapt<TDto>();
|
||||
|
||||
//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<TEntity>();
|
||||
|
||||
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<TEntity>();
|
||||
entity2.Date = entity.Date.AddDays(datesRangeExpected);
|
||||
|
||||
var dbset = dbContext.Set<TEntity>();
|
||||
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<TEntity>();
|
||||
for (var i = 1; i <= differenceBetweenStartAndEndDays; i++)
|
||||
{
|
||||
var entity2 = entity.Adapt<TEntity>();
|
||||
entity2.Date = entity.Date.AddDays(i - 1);
|
||||
|
||||
entities.Add(entity2);
|
||||
}
|
||||
|
||||
var dbset = dbContext.Set<TEntity>();
|
||||
dbset.AddRange(entities);
|
||||
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var response = await timeSeriesClient.GetResampledData(entity.Date.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());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
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;
|
||||
|
||||
namespace DD.Persistence.IntegrationTests.Controllers;
|
||||
public class TimestampedSetControllerTest : BaseIntegrationTest
|
||||
{
|
||||
private readonly ITimestampedSetClient client;
|
||||
|
||||
public TimestampedSetControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||
{
|
||||
var refitClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<IRefitClientFactory<IRefitTimestampedSetClient>>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TimestampedSetClient>>();
|
||||
|
||||
client = scope.ServiceProvider
|
||||
.GetRequiredService<ITimestampedSetClient>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InsertRange()
|
||||
{
|
||||
// arrange
|
||||
Guid idDiscriminator = Guid.NewGuid();
|
||||
IEnumerable<TimestampedSetDto> 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<TimestampedSetDto> 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<TimestampedSetDto> 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.Set);
|
||||
var kv = item.Set.First();
|
||||
Assert.Equal("A", 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<TimestampedSetDto> 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<TimestampedSetDto> 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<TimestampedSetDto> 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<TimestampedSetDto> 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<TimestampedSetDto> 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<TimestampedSetDto> 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<TimestampedSetDto> Generate(int n, DateTimeOffset from)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
yield return new TimestampedSetDto
|
||||
(
|
||||
from.AddSeconds(i),
|
||||
new Dictionary<string, object>{
|
||||
{"A", i },
|
||||
{"B", i * 1.1 },
|
||||
{"C", $"Any{i}" },
|
||||
{"D", DateTimeOffset.Now},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -1,371 +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.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<IRefitClientFactory<IRefitTimestampedValuesClient>>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TimestampedValuesClient>>();
|
||||
|
||||
timestampedValuesClient = scope.ServiceProvider
|
||||
.GetRequiredService<ITimestampedValuesClient>();
|
||||
memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
|
||||
}
|
||||
|
||||
[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<string>() { "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<IEnumerable<TimestampedValuesDto>> AddRange(Guid discriminatorId, int countToCreate = 10)
|
||||
{
|
||||
// arrange
|
||||
IEnumerable<TimestampedValuesDto> 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<TimestampedValuesDto> Generate(int countToCreate, DateTimeOffset from)
|
||||
{
|
||||
var result = new List<TimestampedValuesDto>();
|
||||
for (int i = 0; i < countToCreate; i++)
|
||||
{
|
||||
var values = new Dictionary<string, object>()
|
||||
{
|
||||
{ "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<TimestampedValues>();
|
||||
dbContext.CleanupDbSet<ValuesIdentity>();
|
||||
}
|
||||
}
|
43
DD.Persistence.Models/DD.Persistence.Models.csproj
Normal file
43
DD.Persistence.Models/DD.Persistence.Models.csproj
Normal file
@ -0,0 +1,43 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
|
||||
<!--Генерация NuGet пакета при сборке-->
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<!--Наименование-->
|
||||
<Title>DD.Persistence.Models</Title>
|
||||
<!--Версия пакета-->
|
||||
<VersionPrefix>1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))</VersionPrefix>
|
||||
<!--Версия сборки-->
|
||||
<AssemblyVersion>1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))</AssemblyVersion>
|
||||
<!--Id пакета-->
|
||||
<PackageId>DD.Persistence.Models</PackageId>
|
||||
|
||||
<!--Автор-->
|
||||
<Authors>Digital Drilling</Authors>
|
||||
<!--Компания-->
|
||||
<Company>Digital Drilling</Company>
|
||||
<!--Описание-->
|
||||
<Description>Пакет для получения dtos для работы с Persistence сервисом</Description>
|
||||
|
||||
<!--Url репозитория-->
|
||||
<RepositoryUrl>https://git.ddrilling.ru/on.nemtina/persistence.git</RepositoryUrl>
|
||||
<!--тип репозитория-->
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<!--Символы отладки-->
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<!--Формат пакета с символами-->
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<!--Путь к пакету-->
|
||||
<PackageOutputPath>C:\Projects\Nuget\Persistence\Models</PackageOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
41
DD.Persistence.Models/DataSaubDto.cs
Normal file
41
DD.Persistence.Models/DataSaubDto.cs
Normal file
@ -0,0 +1,41 @@
|
||||
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; }
|
||||
}
|
@ -13,7 +13,7 @@ public class DataSourceSystemDto
|
||||
/// <summary>
|
||||
/// Наименование
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public required string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Описание
|
@ -1,4 +1,4 @@
|
||||
namespace DD.Persistence.Models.Common;
|
||||
namespace DD.Persistence.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Диапазон дат
|
@ -1,12 +1,12 @@
|
||||
namespace DD.Persistence.ModelsAbstractions;
|
||||
namespace DD.Persistence.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс, описывающий временные данные
|
||||
/// </summary>
|
||||
public interface ITimestampAbstractDto
|
||||
public interface ITimeSeriesAbstractDto
|
||||
{
|
||||
/// <summary>
|
||||
/// временная отметка
|
||||
/// </summary>
|
||||
DateTimeOffset Timestamp { get; set; }
|
||||
DateTimeOffset Date { get; set; }
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace DD.Persistence.ModelsAbstractions;
|
||||
namespace DD.Persistence.Models;
|
||||
public interface IWithSectionPart
|
||||
{
|
||||
public double DepthStart { get; set; }
|
@ -1,4 +1,4 @@
|
||||
namespace DD.Persistence.Models.Common;
|
||||
namespace DD.Persistence.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Контейнер для поддержки постраничного просмотра таблиц
|
8
DD.Persistence.Models/TimestampedSetDto.cs
Normal file
8
DD.Persistence.Models/TimestampedSetDto.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace DD.Persistence.Models;
|
||||
|
||||
/// <summary>
|
||||
/// набор данных с отметкой времени
|
||||
/// </summary>
|
||||
/// <param name="Timestamp">отметка времени</param>
|
||||
/// <param name="Set">набор данных</param>
|
||||
public record TimestampedSetDto(DateTimeOffset Timestamp, IDictionary<string, object> Set);
|
@ -6,7 +6,6 @@ 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
|
||||
@ -36,16 +35,15 @@ public static class DependencyInjection
|
||||
|
||||
MapsterSetup();
|
||||
|
||||
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataRepository<DataSaub, DataSaubDto>>();
|
||||
services.AddTransient<ISetpointRepository, SetpointRepository>();
|
||||
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataCachedRepository<DataSaub, DataSaubDto>>();
|
||||
services.AddTransient<IChangeLogRepository, ChangeLogRepository>();
|
||||
services.AddTransient<ITimestampedValuesRepository, TimestampedValuesRepository>();
|
||||
services.AddTransient<ITimestampedSetRepository, TimestampedSetRepository>();
|
||||
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>();
|
||||
|
||||
return services;
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ public static class EFExtensionsSortBy
|
||||
/// и опционально указания направления сортировки "asc" или "desc"
|
||||
/// </param>
|
||||
/// <example>
|
||||
/// var query = query("Timestamp desc");
|
||||
/// var query = query("Date desc");
|
||||
/// </example>
|
||||
/// <returns>Запрос с примененной сортировкой</returns>
|
||||
public static IOrderedQueryable<TSource> SortBy<TSource>(
|
||||
@ -103,7 +103,7 @@ public static class EFExtensionsSortBy
|
||||
/// и опционально указания направления сортировки "asc" или "desc"
|
||||
/// </param>
|
||||
/// <example>
|
||||
/// var query = query("Timestamp desc");
|
||||
/// var query = query("Date desc");
|
||||
/// </example>
|
||||
/// <returns>Запрос с примененной сортировкой</returns>
|
||||
public static IOrderedQueryable<TSource> SortBy<TSource>(
|
||||
@ -129,7 +129,7 @@ public static class EFExtensionsSortBy
|
||||
/// и опционально указания направления сортировки "asc" или "desc"
|
||||
/// </param>
|
||||
/// <example>
|
||||
/// var query = query("Timestamp desc");
|
||||
/// var query = query("Date desc");
|
||||
/// </example>
|
||||
/// <returns>Запрос с примененной сортировкой</returns>
|
||||
public static IOrderedQueryable<TSource> ThenSortBy<TSource>(
|
||||
|
@ -1,32 +0,0 @@
|
||||
namespace DD.Persistence.Repository.Extensions;
|
||||
|
||||
public static class IEnumerableExtensions
|
||||
{
|
||||
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
if (action == null)
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
|
||||
foreach (var item in source)
|
||||
{
|
||||
action(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsNullOrEmpty<T>(this IEnumerable<T>? enumerable)
|
||||
{
|
||||
if (enumerable == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var collection = enumerable as ICollection<T>;
|
||||
if (collection != null)
|
||||
{
|
||||
return collection.Count < 1;
|
||||
}
|
||||
return !enumerable.Any();
|
||||
}
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
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;
|
||||
|
||||
|
@ -5,7 +5,6 @@ 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
|
||||
|
@ -1,27 +1,26 @@
|
||||
using DD.Persistence.Repository.Repositories;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using DD.Persistence.Models;
|
||||
|
||||
namespace DD.Persistence.Repository.RepositoriesCached;
|
||||
public class RelatedDataCachedRepository<TDto, TEntity> : RelatedDataRepository<TDto, TEntity>
|
||||
where TEntity : class, new()
|
||||
where TDto : class, new()
|
||||
namespace DD.Persistence.Repository.Repositories;
|
||||
public class DataSourceSystemCachedRepository : DataSourceSystemRepository
|
||||
{
|
||||
private static readonly string SystemCacheKey = $"{typeof(TEntity).FullName}CacheKey";
|
||||
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 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) =>
|
||||
{
|
@ -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;
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ 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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ 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
|
||||
{
|
||||
@ -36,8 +35,8 @@ namespace DD.Persistence.Repository.Repositories
|
||||
.ToArrayAsync(token);
|
||||
var filteredEntities = entities
|
||||
.GroupBy(e => e.Key)
|
||||
.Select(e => e.OrderBy(o => o.Timestamp))
|
||||
.Select(e => e.Where(e => e.Timestamp <= historyMoment).Last());
|
||||
.Select(e => e.OrderBy(o => o.Created))
|
||||
.Select(e => e.Where(e => e.Created <= historyMoment).Last());
|
||||
var dtos = filteredEntities
|
||||
.Select(e => e.Adapt<SetpointValueDto>());
|
||||
|
||||
@ -48,7 +47,7 @@ namespace DD.Persistence.Repository.Repositories
|
||||
{
|
||||
var query = GetQueryReadOnly();
|
||||
var entities = await query
|
||||
.Where(e => e.Timestamp >= dateBegin)
|
||||
.Where(e => e.Created >= dateBegin)
|
||||
.Take(take)
|
||||
.ToArrayAsync(token);
|
||||
var dtos = entities
|
||||
@ -63,8 +62,8 @@ namespace DD.Persistence.Repository.Repositories
|
||||
.GroupBy(e => 1)
|
||||
.Select(group => new
|
||||
{
|
||||
Min = group.Min(e => e.Timestamp),
|
||||
Max = group.Max(e => e.Timestamp),
|
||||
Min = group.Min(e => e.Created),
|
||||
Max = group.Max(e => e.Created),
|
||||
});
|
||||
var values = await query.FirstOrDefaultAsync(token);
|
||||
var result = new DatesRangeDto()
|
||||
@ -96,7 +95,7 @@ namespace DD.Persistence.Repository.Repositories
|
||||
Key = setpointKey,
|
||||
Value = newValue,
|
||||
IdUser = idUser,
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
Created = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
await db.Set<Setpoint>().AddAsync(entity, token);
|
||||
|
@ -1,19 +1,21 @@
|
||||
using DD.Persistence.Database.Entity;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Common;
|
||||
using DD.Persistence.Models.Requests;
|
||||
using DD.Persistence.Repositories;
|
||||
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.Requests;
|
||||
using DD.Persistence.Repositories;
|
||||
using UuidExtensions;
|
||||
|
||||
namespace DD.Persistence.Repository.Repositories
|
||||
{
|
||||
public class TechMessagesRepository : ITechMessagesRepository
|
||||
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;
|
||||
@ -89,6 +91,7 @@ namespace DD.Persistence.Repository.Repositories
|
||||
await CreateSystemIfNotExist(systemId, token);
|
||||
|
||||
entity.SystemId = systemId;
|
||||
entity.Timestamp = dto.Timestamp.ToUniversalTime();
|
||||
|
||||
entities.Add(entity);
|
||||
}
|
||||
|
@ -0,0 +1,103 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DD.Persistence.Database.Model;
|
||||
using DD.Persistence.Models;
|
||||
|
||||
namespace DD.Persistence.Repository.Repositories;
|
||||
|
||||
public class TimeSeriesDataCachedRepository<TEntity, TDto> : TimeSeriesDataRepository<TEntity, TDto>
|
||||
where TEntity : class, ITimestampedData, new()
|
||||
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||
{
|
||||
public static TDto? FirstByDate { get; private set; }
|
||||
public static CyclicArray<TDto> LastData { get; } = new CyclicArray<TDto>(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<IEnumerable<TDto>> 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<int> AddRange(IEnumerable<TDto> 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<DatesRangeDto?> 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<IEnumerable<TDto>> 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;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
using Mapster;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DD.Persistence.Database.Model;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Repositories;
|
||||
|
||||
namespace DD.Persistence.Repository.Repositories;
|
||||
public class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataRepository<TDto>
|
||||
where TEntity : class, ITimestampedData, new()
|
||||
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||
{
|
||||
private readonly DbContext db;
|
||||
|
||||
public TimeSeriesDataRepository(DbContext db)
|
||||
{
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
protected virtual IQueryable<TEntity> GetQueryReadOnly() => this.db.Set<TEntity>();
|
||||
|
||||
public virtual async Task<DatesRangeDto?> GetDatesRange(CancellationToken token)
|
||||
{
|
||||
var query = GetQueryReadOnly();
|
||||
var minDate = await query.MinAsync(o => o.Date, token);
|
||||
var maxDate = await query.MaxAsync(o => o.Date, token);
|
||||
|
||||
return new DatesRangeDto
|
||||
{
|
||||
From = minDate,
|
||||
To = maxDate
|
||||
};
|
||||
}
|
||||
|
||||
public virtual async Task<IEnumerable<TDto>> GetGtDate(DateTimeOffset date, CancellationToken token)
|
||||
{
|
||||
var query = this.db.Set<TEntity>().Where(e => e.Date > date);
|
||||
var entities = await query.ToArrayAsync(token);
|
||||
|
||||
var dtos = entities.Select(e => e.Adapt<TDto>());
|
||||
|
||||
return dtos;
|
||||
}
|
||||
|
||||
public virtual async Task<int> AddRange(IEnumerable<TDto> dtos, CancellationToken token)
|
||||
{
|
||||
var entities = dtos.Select(d => d.Adapt<TEntity>());
|
||||
|
||||
await db.Set<TEntity>().AddRangeAsync(entities, token);
|
||||
var result = await db.SaveChangesAsync(token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected async Task<IEnumerable<TDto>> GetLastAsync(int takeCount, CancellationToken token)
|
||||
{
|
||||
var query = GetQueryReadOnly()
|
||||
.OrderByDescending(e => e.Date)
|
||||
.Take(takeCount);
|
||||
|
||||
var entities = await query.ToArrayAsync(token);
|
||||
var dtos = entities.Select(e => e.Adapt<TDto>());
|
||||
|
||||
return dtos;
|
||||
}
|
||||
|
||||
protected async Task<TDto?> GetFirstAsync(CancellationToken token)
|
||||
{
|
||||
var query = GetQueryReadOnly()
|
||||
.OrderBy(e => e.Date);
|
||||
|
||||
var entity = await query.FirstOrDefaultAsync(token);
|
||||
|
||||
if (entity == null)
|
||||
return null;
|
||||
|
||||
var dto = entity.Adapt<TDto>();
|
||||
return dto;
|
||||
}
|
||||
|
||||
public async virtual Task<IEnumerable<TDto>> GetResampledData(
|
||||
DateTimeOffset dateBegin,
|
||||
double intervalSec = 600d,
|
||||
int approxPointsCount = 1024,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var dtos = await 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DD.Persistence.Database.Entity;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Repositories;
|
||||
|
||||
namespace DD.Persistence.Repository.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Репозиторий для хранения разных наборов данных временных рядов.
|
||||
/// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest.
|
||||
/// idDiscriminator формируют клиенты и только им известно что они обозначают.
|
||||
/// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено.
|
||||
/// </summary>
|
||||
public class TimestampedSetRepository : ITimestampedSetRepository
|
||||
{
|
||||
private readonly DbContext db;
|
||||
|
||||
public TimestampedSetRepository(DbContext db)
|
||||
{
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
public Task<int> AddRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token)
|
||||
{
|
||||
var entities = sets.Select(set => new TimestampedSet(idDiscriminator, set.Timestamp.ToUniversalTime(), set.Set));
|
||||
var dbSet = db.Set<TimestampedSet>();
|
||||
dbSet.AddRange(entities);
|
||||
return db.SaveChangesAsync(token);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TimestampedSetDto>> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
||||
{
|
||||
var dbSet = db.Set<TimestampedSet>();
|
||||
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<IEnumerable<TimestampedSetDto>> GetLast(Guid idDiscriminator, IEnumerable<string>? columnNames, int take, CancellationToken token)
|
||||
{
|
||||
var dbSet = db.Set<TimestampedSet>();
|
||||
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<int> Count(Guid idDiscriminator, CancellationToken token)
|
||||
{
|
||||
var dbSet = db.Set<TimestampedSet>();
|
||||
var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator);
|
||||
return query.CountAsync(token);
|
||||
}
|
||||
|
||||
public async Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token)
|
||||
{
|
||||
var query = db.Set<TimestampedSet>()
|
||||
.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<IEnumerable<TimestampedSetDto>> Materialize(IQueryable<TimestampedSet> 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<TimestampedSet> ApplyGeTimestamp(IQueryable<TimestampedSet> query, DateTimeOffset geTimestamp)
|
||||
{
|
||||
var geTimestampUtc = geTimestamp.ToUniversalTime();
|
||||
return query.Where(entity => entity.Timestamp >= geTimestampUtc);
|
||||
}
|
||||
|
||||
private static IEnumerable<TimestampedSetDto> ReduceSetColumnsByNames(IEnumerable<TimestampedSetDto> query, IEnumerable<string> 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;
|
||||
}
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
using DD.Persistence.Database.Entity;
|
||||
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)
|
||||
{
|
||||
this.db = db;
|
||||
this.relatedDataRepository = relatedDataRepository;
|
||||
}
|
||||
|
||||
protected virtual IQueryable<TimestampedValues> GetQueryReadOnly() => this.db.Set<TimestampedValues>()
|
||||
.Include(e => e.ValuesIdentity);
|
||||
|
||||
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,
|
||||
Timestamp = dto.Timestamp.ToUniversalTime(),
|
||||
Values = dto.Values.Values.ToArray()
|
||||
};
|
||||
timestampedValuesEntities.Add(timestampedValuesEntity);
|
||||
}
|
||||
|
||||
await db.Set<TimestampedValues>().AddRangeAsync(timestampedValuesEntities, token);
|
||||
|
||||
var result = await db.SaveChangesAsync(token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async virtual Task<IEnumerable<TimestampedValuesDto>> Get(Guid discriminatorId, DateTimeOffset? timestampBegin, IEnumerable<string>? 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<IEnumerable<TimestampedValuesDto>> 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<IEnumerable<TimestampedValuesDto>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token)
|
||||
{
|
||||
var query = GetQueryReadOnly()
|
||||
.OrderByDescending(e => e.Timestamp)
|
||||
.Take(takeCount);
|
||||
|
||||
var dtos = await Materialize(discriminatorId, query, token);
|
||||
|
||||
return dtos;
|
||||
}
|
||||
|
||||
public async virtual Task<IEnumerable<TimestampedValuesDto>> GetResampledData(
|
||||
Guid discriminatorId,
|
||||
DateTimeOffset dateBegin,
|
||||
double intervalSec = 600d,
|
||||
int approxPointsCount = 1024,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var dtos = await GetGtDate(discriminatorId, 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;
|
||||
}
|
||||
|
||||
public async virtual Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
|
||||
{
|
||||
var query = GetQueryReadOnly()
|
||||
.Where(e => e.Timestamp > timestampBegin);
|
||||
|
||||
var dtos = await Materialize(discriminatorId, query, token);
|
||||
|
||||
return dtos;
|
||||
}
|
||||
|
||||
public async virtual Task<DatesRangeDto?> 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;
|
||||
}
|
||||
|
||||
var dto = new DatesRangeDto
|
||||
{
|
||||
From = item.Min,
|
||||
To = item.Max,
|
||||
};
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
public virtual Task<int> Count(Guid discriminatorId, CancellationToken token)
|
||||
{
|
||||
var dbSet = db.Set<TimestampedValues>();
|
||||
var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private IQueryable<TimestampedValues> ApplyGeTimestamp(IQueryable<TimestampedValues> query, DateTimeOffset timestampBegin)
|
||||
{
|
||||
var geTimestampUtc = timestampBegin.ToUniversalTime();
|
||||
|
||||
var result = query
|
||||
.Where(entity => entity.Timestamp >= geTimestampUtc);
|
||||
|
||||
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}]");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
//using DD.Persistence.Models;
|
||||
//using DD.Persistence.Models.Common;
|
||||
//using DD.Persistence.Repositories;
|
||||
//using Microsoft.EntityFrameworkCore;
|
||||
|
||||
//namespace DD.Persistence.Repository.Repositories;
|
||||
|
||||
//public class TimestampedValuesCachedRepository : TimestampedValuesRepository
|
||||
//{
|
||||
// public static TimestampedValuesDto? FirstByDate { get; private set; }
|
||||
// public static CyclicArray<TimestampedValuesDto> LastData { get; } = new CyclicArray<TimestampedValuesDto>(CacheItemsCount);
|
||||
|
||||
// private const int CacheItemsCount = 3600;
|
||||
|
||||
// public TimestampedValuesCachedRepository(DbContext db, IRelatedDataRepository<ValuesIdentityDto> relatedDataRepository) : base(db, relatedDataRepository)
|
||||
// {
|
||||
// //Task.Run(async () =>
|
||||
// //{
|
||||
// // var firstDateItem = await base.GetFirst(CancellationToken.None);
|
||||
// // if (firstDateItem == null)
|
||||
// // {
|
||||
// // return;
|
||||
// // }
|
||||
|
||||
// // FirstByDate = firstDateItem;
|
||||
|
||||
// // var dtos = await base.GetLast(CacheItemsCount, CancellationToken.None);
|
||||
// // dtos = dtos.OrderBy(d => d.Timestamp);
|
||||
// // LastData.AddRange(dtos);
|
||||
// //}).Wait();
|
||||
// }
|
||||
|
||||
// public override async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token)
|
||||
// {
|
||||
|
||||
// if (LastData.Count == 0 || LastData[0].Timestamp > dateBegin)
|
||||
// {
|
||||
// var dtos = await base.GetGtDate(discriminatorId, dateBegin, token);
|
||||
// return dtos;
|
||||
// }
|
||||
|
||||
// var items = LastData
|
||||
// .Where(i => i.Timestamp >= dateBegin);
|
||||
|
||||
// return items;
|
||||
// }
|
||||
|
||||
// public override async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
|
||||
// {
|
||||
// var result = await base.AddRange(discriminatorId, dtos, token);
|
||||
// if (result > 0)
|
||||
// {
|
||||
|
||||
// dtos = dtos.OrderBy(x => x.Timestamp);
|
||||
|
||||
// FirstByDate = dtos.First();
|
||||
// LastData.AddRange(dtos);
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// public override async Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, 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<IEnumerable<TimestampedValuesDto>> GetResampledData(
|
||||
// Guid discriminatorId,
|
||||
// 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(discriminatorId, 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;
|
||||
// }
|
||||
//}
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using DD.Persistence.Models.Common;
|
||||
using DD.Persistence.Models;
|
||||
|
||||
namespace DD.Persistence.API;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user