Compare commits
15 Commits
test/Telem
...
master
Author | SHA1 | Date | |
---|---|---|---|
3d6eb1a28c | |||
|
8596f5b35c | ||
|
f844656ed0 | ||
|
9a281238e9 | ||
56a6bd0a67 | |||
4c2b91cd2d | |||
649c51a8ab | |||
4cae12ddac | |||
6873a32667 | |||
|
4ec4ec17eb | ||
|
d0cd647849 | ||
|
6593acbc20 | ||
01898e84f6 | |||
441f48b4c6 | |||
76e9f56107 |
@ -31,7 +31,7 @@ jobs:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 9.0.x
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
- name: Run integration tests
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
@ -16,10 +16,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.2.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.3.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -16,12 +16,6 @@ namespace DD.Persistence.API;
|
||||
|
||||
public static class DependencyInjection
|
||||
{
|
||||
//public static void MapsterSetup()
|
||||
//{
|
||||
// TypeAdapterConfig.GlobalSettings.Default.Config
|
||||
// .ForType<TechMessageDto, TechMessage>()
|
||||
// .Ignore(dest => dest.System, dest => dest.SystemId);
|
||||
//}
|
||||
public static void AddSwagger(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddSwaggerGen(c =>
|
||||
|
@ -26,8 +26,6 @@ public class Startup
|
||||
services.AddJWTAuthentication(Configuration);
|
||||
services.AddMemoryCache();
|
||||
services.AddServices();
|
||||
|
||||
//DependencyInjection.MapsterSetup();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>8dcdcfed-c959-4eef-9891-ae60b1b136ea</UserSecretsId>
|
||||
@ -14,9 +14,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -19,6 +19,7 @@
|
||||
"password": 12345,
|
||||
"clientId": "webapi",
|
||||
"grantType": "password",
|
||||
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "7d9f3574-6574-4ca3-845a-0276eb4aa8f6"
|
||||
}
|
||||
"http://schemas.xmlsoap.org/ws/2005/05/identity /claims/nameidentifier": "7d9f3574-6574-4ca3-845a-0276eb4aa8f6"
|
||||
},
|
||||
"ClientUrl": "http://localhost:5000/"
|
||||
}
|
||||
|
@ -3,16 +3,17 @@ using DD.Persistence.Client.Clients.Base;
|
||||
using DD.Persistence.Client.Clients.Interfaces;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Requests;
|
||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients;
|
||||
public class ChangeLogClient : BaseClient, IChangeLogClient
|
||||
{
|
||||
private readonly Interfaces.Refit.IRefitChangeLogClient refitChangeLogClient;
|
||||
private readonly IRefitChangeLogClient refitChangeLogClient;
|
||||
|
||||
public ChangeLogClient(Interfaces.Refit.IRefitChangeLogClient refitChangeLogClient, ILogger<ChangeLogClient> logger) : base(logger)
|
||||
public ChangeLogClient(IRefitClientFactory<IRefitChangeLogClient> refitClientFactory, ILogger<ChangeLogClient> logger) : base(logger)
|
||||
{
|
||||
this.refitChangeLogClient = refitChangeLogClient;
|
||||
}
|
||||
this.refitChangeLogClient = refitClientFactory.Create();
|
||||
}
|
||||
|
||||
public async Task<int> ClearAndAddRange(Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token)
|
||||
{
|
||||
|
@ -9,9 +9,9 @@ public class DataSourceSystemClient : BaseClient, IDataSourceSystemClient
|
||||
{
|
||||
private readonly IRefitDataSourceSystemClient dataSourceSystemClient;
|
||||
|
||||
public DataSourceSystemClient(IRefitDataSourceSystemClient dataSourceSystemClient, ILogger<DataSourceSystemClient> logger) : base(logger)
|
||||
public DataSourceSystemClient(IRefitClientFactory<IRefitDataSourceSystemClient> dataSourceSystemClientFactory, ILogger<DataSourceSystemClient> logger) : base(logger)
|
||||
{
|
||||
this.dataSourceSystemClient = dataSourceSystemClient;
|
||||
this.dataSourceSystemClient = dataSourceSystemClientFactory.Create();
|
||||
}
|
||||
|
||||
public async Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token)
|
||||
|
@ -6,7 +6,7 @@ namespace DD.Persistence.Client.Clients.Interfaces;
|
||||
/// Клиент для работы с временными данными
|
||||
/// </summary>
|
||||
/// <typeparam name="TDto"></typeparam>
|
||||
public interface ITimeSeriesClient<TDto> : IDisposable where TDto : class, new()
|
||||
public interface ITimeSeriesClient<TDto> : IDisposable where TDto : class, ITimeSeriesAbstractDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Добавление записей
|
||||
|
@ -4,7 +4,7 @@ using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
|
||||
public interface IRefitChangeLogClient : IDisposable
|
||||
public interface IRefitChangeLogClient : IRefitClient, IDisposable
|
||||
{
|
||||
private const string BaseRoute = "/api/ChangeLog";
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
public interface IRefitClient
|
||||
{
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
public interface IRefitDataSourceSystemClient : IDisposable
|
||||
public interface IRefitDataSourceSystemClient : IRefitClient, IDisposable
|
||||
{
|
||||
private const string BaseRoute = "/api/dataSourceSystem";
|
||||
|
||||
|
@ -3,7 +3,7 @@ using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
|
||||
public interface IRefitSetpointClient : IDisposable
|
||||
public interface IRefitSetpointClient : IRefitClient, IDisposable
|
||||
{
|
||||
private const string BaseRoute = "/api/setpoint";
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models.Requests;
|
||||
using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit
|
||||
{
|
||||
public interface IRefitTechMessagesClient : IDisposable
|
||||
public interface IRefitTechMessagesClient : IRefitClient, IDisposable
|
||||
{
|
||||
private const string BaseRoute = "/api/techMessages";
|
||||
|
||||
|
@ -2,8 +2,8 @@ using DD.Persistence.Models;
|
||||
using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
public interface IRefitTimeSeriesClient<TDto> : IDisposable
|
||||
where TDto : class, new()
|
||||
public interface IRefitTimeSeriesClient<TDto> : IRefitClient, IDisposable
|
||||
where TDto : class, ITimeSeriesAbstractDto
|
||||
{
|
||||
private const string BaseRoute = "/api/dataSaub";
|
||||
|
||||
|
@ -3,7 +3,7 @@ using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
|
||||
public interface IRefitTimestampedSetClient : IDisposable
|
||||
public interface IRefitTimestampedSetClient : IRefitClient, IDisposable
|
||||
{
|
||||
private const string baseUrl = "/api/TimestampedSet/{idDiscriminator}";
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using DD.Persistence.Models;
|
||||
using DD.Persistence.Models;
|
||||
using Refit;
|
||||
|
||||
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
public interface IRefitWitsDataClient : IDisposable
|
||||
public interface IRefitWitsDataClient : IRefitClient, IDisposable
|
||||
{
|
||||
private const string BaseRoute = "/api/witsData";
|
||||
|
||||
|
@ -10,9 +10,9 @@ public class SetpointClient : BaseClient, ISetpointClient
|
||||
{
|
||||
private readonly IRefitSetpointClient refitSetpointClient;
|
||||
|
||||
public SetpointClient(IRefitSetpointClient refitSetpointClient, ILogger<SetpointClient> logger) : base(logger)
|
||||
public SetpointClient(IRefitClientFactory<IRefitSetpointClient> refitSetpointClientFactory, ILogger<SetpointClient> logger) : base(logger)
|
||||
{
|
||||
this.refitSetpointClient = refitSetpointClient;
|
||||
this.refitSetpointClient = refitSetpointClientFactory.Create();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token)
|
||||
|
@ -11,9 +11,9 @@ public class TechMessagesClient : BaseClient, ITechMessagesClient
|
||||
{
|
||||
private readonly IRefitTechMessagesClient refitTechMessagesClient;
|
||||
|
||||
public TechMessagesClient(IRefitTechMessagesClient refitTechMessagesClient, ILogger<TechMessagesClient> logger) : base(logger)
|
||||
public TechMessagesClient(IRefitClientFactory<IRefitTechMessagesClient> refitTechMessagesClientFactory, ILogger<TechMessagesClient> logger) : base(logger)
|
||||
{
|
||||
this.refitTechMessagesClient = refitTechMessagesClient;
|
||||
this.refitTechMessagesClient = refitTechMessagesClientFactory.Create();
|
||||
}
|
||||
|
||||
public async Task<PaginationContainer<TechMessageDto>> GetPage(PaginationRequest request, CancellationToken token)
|
||||
|
@ -5,13 +5,13 @@ 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, new()
|
||||
public class TimeSeriesClient<TDto> : BaseClient, ITimeSeriesClient<TDto> where TDto : class, ITimeSeriesAbstractDto
|
||||
{
|
||||
private readonly IRefitTimeSeriesClient<TDto> timeSeriesClient;
|
||||
|
||||
public TimeSeriesClient(IRefitTimeSeriesClient<TDto> refitTechMessagesClient, ILogger<TimeSeriesClient<TDto>> logger) : base(logger)
|
||||
public TimeSeriesClient(IRefitClientFactory<IRefitTimeSeriesClient<TDto>> refitTechMessagesClientFactory, ILogger<TimeSeriesClient<TDto>> logger) : base(logger)
|
||||
{
|
||||
this.timeSeriesClient = refitTechMessagesClient;
|
||||
this.timeSeriesClient = refitTechMessagesClientFactory.Create();
|
||||
}
|
||||
|
||||
public async Task<int> AddRange(IEnumerable<TDto> dtos, CancellationToken token)
|
||||
|
@ -9,9 +9,9 @@ public class TimestampedSetClient : BaseClient, ITimestampedSetClient
|
||||
{
|
||||
private readonly IRefitTimestampedSetClient refitTimestampedSetClient;
|
||||
|
||||
public TimestampedSetClient(IRefitTimestampedSetClient refitTimestampedSetClient, ILogger<TimestampedSetClient> logger) : base(logger)
|
||||
public TimestampedSetClient(IRefitClientFactory<IRefitTimestampedSetClient> refitTimestampedSetClientFactory, ILogger<TimestampedSetClient> logger) : base(logger)
|
||||
{
|
||||
this.refitTimestampedSetClient = refitTimestampedSetClient;
|
||||
this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create();
|
||||
}
|
||||
|
||||
public async Task<int> AddRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token)
|
||||
|
@ -9,9 +9,9 @@ public class WitsDataClient : BaseClient, IWitsDataClient
|
||||
{
|
||||
private readonly IRefitWitsDataClient refitWitsDataClient;
|
||||
|
||||
public WitsDataClient(IRefitWitsDataClient refitWitsDataClient, ILogger<WitsDataClient> logger) : base(logger)
|
||||
public WitsDataClient(IRefitClientFactory<IRefitWitsDataClient> refitWitsDataClientFactory, ILogger<WitsDataClient> logger) : base(logger)
|
||||
{
|
||||
this.refitWitsDataClient = refitWitsDataClient;
|
||||
this.refitWitsDataClient = refitWitsDataClientFactory.Create();
|
||||
}
|
||||
|
||||
public async Task<int> AddRange(IEnumerable<WitsDataDto> dtos, CancellationToken token)
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
@ -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>
|
||||
@ -49,15 +49,16 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.2.1" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.0" />
|
||||
<PackageReference Include="Refit" Version="8.0.0" />
|
||||
<PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" />
|
||||
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.1" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="9.0.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DD.Persistence\DD.Persistence.csproj" />
|
||||
<ProjectReference Include="..\DD.Persistence.Models\DD.Persistence.Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
30
DD.Persistence.Client/DependencyInjection.cs
Normal file
30
DD.Persistence.Client/DependencyInjection.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using DD.Persistence.Client.Clients;
|
||||
using DD.Persistence.Client.Clients.Interfaces;
|
||||
using DD.Persistence.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace DD.Persistence.Client;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddPersistenceClients(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient(typeof(IRefitClientFactory<>), typeof(RefitClientFactory<>));
|
||||
services.AddTransient<IChangeLogClient, ChangeLogClient>();
|
||||
services.AddTransient<IDataSourceSystemClient, DataSourceSystemClient>();
|
||||
services.AddTransient<ISetpointClient, SetpointClient>();
|
||||
services.AddTransient<ITechMessagesClient, TechMessagesClient>();
|
||||
services.AddTransient<ITimeSeriesClient<DataSaubDto>, TimeSeriesClient<DataSaubDto>>();
|
||||
services.AddTransient<ITimestampedSetClient, TimestampedSetClient>();
|
||||
services.AddTransient<IWitsDataClient, WitsDataClient>();
|
||||
return services;
|
||||
}
|
||||
}
|
16
DD.Persistence.Client/IRefitClientFactory.cs
Normal file
16
DD.Persistence.Client/IRefitClientFactory.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
|
||||
namespace DD.Persistence.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс для фабрики, которая создает refit-клиентов
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IRefitClientFactory<T> where T : IRefitClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Создание refit-клиента
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public T Create();
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using DD.Persistence.Client.Clients.Interfaces;
|
||||
using DD.Persistence.Client.Clients;
|
||||
using DD.Persistence.Client.Helpers;
|
||||
using Refit;
|
||||
using DD.Persistence.Factories;
|
||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DD.Persistence.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Фабрика клиентов для доступа к Persistence - сервису
|
||||
/// </summary>
|
||||
public class PersistenceClientFactory
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
private static readonly RefitSettings RefitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions));
|
||||
private readonly IServiceProvider provider;
|
||||
private HttpClient httpClient;
|
||||
public PersistenceClientFactory(IHttpClientFactory httpClientFactory, IServiceProvider provider, IConfiguration configuration)
|
||||
{
|
||||
this.provider = provider;
|
||||
|
||||
httpClient = httpClientFactory.CreateClient();
|
||||
|
||||
httpClient.Authorize(configuration);
|
||||
}
|
||||
|
||||
public PersistenceClientFactory(IHttpClientFactory httpClientFactory, IAuthTokenFactory authTokenFactory, IServiceProvider provider, IConfiguration configuration)
|
||||
{
|
||||
this.provider = provider;
|
||||
|
||||
httpClient = httpClientFactory.CreateClient();
|
||||
|
||||
var token = authTokenFactory.GetToken();
|
||||
httpClient.Authorize(token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить клиент для работы с уставками
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ISetpointClient GetSetpointClient()
|
||||
{
|
||||
var logger = provider.GetRequiredService<ILogger<SetpointClient>>();
|
||||
|
||||
var restClient = RestService.For<IRefitSetpointClient>(httpClient, RefitSettings);
|
||||
var client = new SetpointClient(restClient, logger);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить клиент для работы с технологическими сообщениями
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ITechMessagesClient GetTechMessagesClient()
|
||||
{
|
||||
var logger = provider.GetRequiredService<ILogger<TechMessagesClient>>();
|
||||
|
||||
var restClient = RestService.For<IRefitTechMessagesClient>(httpClient, RefitSettings);
|
||||
var client = new TechMessagesClient(restClient, logger);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить клиент для работы с временными данными
|
||||
/// </summary>
|
||||
/// <typeparam name="TDto"></typeparam>
|
||||
/// <returns></returns>
|
||||
public ITimeSeriesClient<TDto> GetTimeSeriesClient<TDto>()
|
||||
where TDto : class, new()
|
||||
{
|
||||
var logger = provider.GetRequiredService<ILogger<TimeSeriesClient<TDto>>>();
|
||||
|
||||
var restClient = RestService.For<IRefitTimeSeriesClient<TDto>>(httpClient, RefitSettings);
|
||||
var client = new TimeSeriesClient<TDto>(restClient, logger);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить клиент для работы с данными с отметкой времени
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ITimestampedSetClient GetTimestampedSetClient()
|
||||
{
|
||||
var logger = provider.GetRequiredService<ILogger<TimestampedSetClient>>();
|
||||
|
||||
var restClient = RestService.For<IRefitTimestampedSetClient>(httpClient, RefitSettings);
|
||||
var client = new TimestampedSetClient(restClient, logger);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить клиент для работы с записями ChangeLog
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IChangeLogClient GetChangeLogClient()
|
||||
{
|
||||
var logger = provider.GetRequiredService<ILogger<ChangeLogClient>>();
|
||||
|
||||
var restClient = RestService.For<IRefitChangeLogClient>(httpClient, RefitSettings);
|
||||
var client = new ChangeLogClient(restClient, logger);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить клиент для работы c параметрами Wits
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IWitsDataClient GetWitsDataClient()
|
||||
{
|
||||
var logger = provider.GetRequiredService<ILogger<WitsDataClient>>();
|
||||
|
||||
var restClient = RestService.For<IRefitWitsDataClient>(httpClient, RefitSettings);
|
||||
var client = new WitsDataClient(restClient, logger);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить клиент для работы c системами
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IDataSourceSystemClient GetDataSourceSystemClient()
|
||||
{
|
||||
var logger = provider.GetRequiredService<ILogger<DataSourceSystemClient>>();
|
||||
|
||||
var restClient = RestService.For<IRefitDataSourceSystemClient>(httpClient, RefitSettings);
|
||||
var client = new DataSourceSystemClient(restClient, logger);
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
52
DD.Persistence.Client/RefitClientFactory.cs
Normal file
52
DD.Persistence.Client/RefitClientFactory.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
using DD.Persistence.Client.Helpers;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Refit;
|
||||
using System.Configuration;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DD.Persistence.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Фабрика, которая создает refit-клиентов
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class RefitClientFactory<T> : IRefitClientFactory<T> where T : IRefitClient
|
||||
{
|
||||
private HttpClient client;
|
||||
private RefitSettings refitSettings;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public RefitClientFactory(IConfiguration configuration, ILogger<IRefitClientFactory<T>> logger, HttpClient client)
|
||||
{
|
||||
//this.client = factory.CreateClient();
|
||||
this.client = client;
|
||||
|
||||
var baseUrl = configuration.GetSection("ClientUrl").Get<string>();
|
||||
if (String.IsNullOrEmpty(baseUrl))
|
||||
{
|
||||
var exception = new SettingsPropertyNotFoundException("В настройках конфигурации не указан адрес Persistence сервиса.");
|
||||
|
||||
logger.LogError(exception.Message);
|
||||
|
||||
throw exception;
|
||||
}
|
||||
client.BaseAddress = new Uri(baseUrl);
|
||||
|
||||
JsonSerializerOptions JsonSerializerOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
refitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions));
|
||||
}
|
||||
/// <summary>
|
||||
/// создание клиента
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public T Create()
|
||||
{
|
||||
return RestService.For<T>(client, refitSettings);
|
||||
}
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,14 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -1,56 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace DD.Persistence.Database.Entity;
|
||||
|
||||
public interface ITimestamped
|
||||
{
|
||||
DateTimeOffset Timestamp { get; set; }
|
||||
}
|
||||
|
||||
[PrimaryKey(nameof(ParameterId), nameof(Timestamp))]
|
||||
public class TagValue: ITimestamped
|
||||
{
|
||||
[Comment("Id параметра")]
|
||||
public int ParameterId { get; set; }
|
||||
|
||||
[Comment("Временная отметка")]
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
|
||||
[Comment("Значение параметра")]
|
||||
public float Value { get; set; }
|
||||
}
|
||||
|
||||
public class TagSetValue: ITimestamped
|
||||
{
|
||||
[Comment("Временная отметка"), Key]
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
public float WellDepth {get;set;}
|
||||
public float BitDepth {get;set;}
|
||||
public float BlockPosition {get;set;}
|
||||
public float BlockSpeed {get;set;}
|
||||
public float Pressure {get;set;}
|
||||
public float AxialLoad {get;set;}
|
||||
public float HookWeight {get;set;}
|
||||
public float RotorTorque {get;set;}
|
||||
public float RotorSpeed {get;set;}
|
||||
public float Flow {get;set;}
|
||||
public int Mse {get;set;}
|
||||
public int Mode {get;set;}
|
||||
public float Pump0Flow {get;set;}
|
||||
public float Pump1Flow {get;set;}
|
||||
public float Pump2Flow { get; set; }
|
||||
}
|
||||
|
||||
[PrimaryKey(nameof(BagId), nameof(Timestamp))]
|
||||
public class TagBag : ITimestamped
|
||||
{
|
||||
[Comment("Временная отметка"), Key]
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
|
||||
public Guid BagId { get; set; }
|
||||
|
||||
[Column(TypeName = "jsonb")]
|
||||
public object[] Values { get; set; }
|
||||
}
|
@ -11,10 +11,6 @@ public class PersistenceDbContext : DbContext
|
||||
{
|
||||
public DbSet<DataSaub> DataSaub => Set<DataSaub>();
|
||||
|
||||
public DbSet<TagValue> TagValues => Set<TagValue>();
|
||||
public DbSet<TagSetValue> TagSetValues => Set<TagSetValue>();
|
||||
public DbSet<TagBag> TagBag => Set<TagBag>();
|
||||
|
||||
public DbSet<Setpoint> Setpoint => Set<Setpoint>();
|
||||
|
||||
public DbSet<TimestampedSet> TimestampedSets => Set<TimestampedSet>();
|
||||
@ -42,9 +38,5 @@ public class PersistenceDbContext : DbContext
|
||||
modelBuilder.Entity<ChangeLog>()
|
||||
.Property(e => e.Value)
|
||||
.HasJsonConversion();
|
||||
|
||||
modelBuilder.Entity<TagBag>()
|
||||
.Property(e => e.Values)
|
||||
.HasJsonConversion();
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,11 @@ using DD.Persistence.Models.Requests;
|
||||
using Xunit;
|
||||
using DD.Persistence.Client.Clients.Interfaces;
|
||||
using DD.Persistence.Client;
|
||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
using DD.Persistence.Client.Clients;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Refit;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace DD.Persistence.IntegrationTests.Controllers;
|
||||
public class ChangeLogControllerTest : BaseIntegrationTest
|
||||
@ -16,10 +21,12 @@ public class ChangeLogControllerTest : BaseIntegrationTest
|
||||
|
||||
public ChangeLogControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||
{
|
||||
var persistenceClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<PersistenceClientFactory>();
|
||||
var refitClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<IRefitClientFactory<IRefitChangeLogClient>>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<ChangeLogClient>>();
|
||||
|
||||
client = persistenceClientFactory.GetChangeLogClient();
|
||||
client = scope.ServiceProvider
|
||||
.GetRequiredService<IChangeLogClient>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -6,6 +6,8 @@ using DD.Persistence.Client.Clients.Interfaces;
|
||||
using DD.Persistence.Database.Entity;
|
||||
using DD.Persistence.Models;
|
||||
using Xunit;
|
||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DD.Persistence.IntegrationTests.Controllers
|
||||
{
|
||||
@ -16,11 +18,12 @@ namespace DD.Persistence.IntegrationTests.Controllers
|
||||
private readonly IMemoryCache memoryCache;
|
||||
public DataSourceSystemControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||
{
|
||||
var scope = factory.Services.CreateScope();
|
||||
var persistenceClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<PersistenceClientFactory>();
|
||||
var refitClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<IRefitClientFactory<IRefitDataSourceSystemClient>>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<DataSourceSystemClient>>();
|
||||
|
||||
dataSourceSystemClient = persistenceClientFactory.GetDataSourceSystemClient();
|
||||
dataSourceSystemClient = scope.ServiceProvider
|
||||
.GetRequiredService<IDataSourceSystemClient>();
|
||||
memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,9 @@ using DD.Persistence.Client.Clients.Interfaces;
|
||||
using DD.Persistence.Database.Model;
|
||||
using System.Net;
|
||||
using Xunit;
|
||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||
using DD.Persistence.Client.Clients;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DD.Persistence.IntegrationTests.Controllers
|
||||
{
|
||||
@ -17,11 +20,12 @@ namespace DD.Persistence.IntegrationTests.Controllers
|
||||
}
|
||||
public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||
{
|
||||
var scope = factory.Services.CreateScope();
|
||||
var persistenceClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<PersistenceClientFactory>();
|
||||
var refitClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<IRefitClientFactory<IRefitSetpointClient>>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<SetpointClient>>();
|
||||
|
||||
setpointClient = persistenceClientFactory.GetSetpointClient();
|
||||
setpointClient = scope.ServiceProvider
|
||||
.GetRequiredService<ISetpointClient>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1,38 +1,41 @@
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using DD.Persistence.Client;
|
||||
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 DD.Persistence.Models.Enumerations;
|
||||
using DD.Persistence.Models.Requests;
|
||||
using System.Net;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace DD.Persistence.IntegrationTests.Controllers
|
||||
{
|
||||
public class TechMessagesControllerTest : BaseIntegrationTest
|
||||
{
|
||||
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)
|
||||
{
|
||||
var scope = factory.Services.CreateScope();
|
||||
var persistenceClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<PersistenceClientFactory>();
|
||||
public class TechMessagesControllerTest : BaseIntegrationTest
|
||||
{
|
||||
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)
|
||||
{
|
||||
var refitClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<IRefitClientFactory<IRefitTechMessagesClient>>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TechMessagesClient>>();
|
||||
|
||||
techMessagesClient = persistenceClientFactory.GetTechMessagesClient();
|
||||
memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
|
||||
}
|
||||
techMessagesClient = scope.ServiceProvider
|
||||
.GetRequiredService<ITechMessagesClient>();
|
||||
memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPage_returns_success()
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<DataSourceSystem>();
|
||||
[Fact]
|
||||
public async Task GetPage_returns_success()
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<DataSourceSystem>();
|
||||
|
||||
var requestDto = new PaginationRequest()
|
||||
{
|
||||
@ -44,235 +47,235 @@ namespace DD.Persistence.IntegrationTests.Controllers
|
||||
//act
|
||||
var response = await techMessagesClient.GetPage(requestDto, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.Empty(response.Items);
|
||||
Assert.Equal(requestDto.Skip, response.Skip);
|
||||
Assert.Equal(requestDto.Take, response.Take);
|
||||
}
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.Empty(response.Items);
|
||||
Assert.Equal(requestDto.Skip, response.Skip);
|
||||
Assert.Equal(requestDto.Take, response.Take);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPage_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
var dtos = await InsertRange(Guid.NewGuid());
|
||||
var dtosCount = dtos.Count();
|
||||
var requestDto = new PaginationRequest()
|
||||
{
|
||||
Skip = 0,
|
||||
Take = 2,
|
||||
SortSettings = nameof(TechMessage.CategoryId)
|
||||
};
|
||||
[Fact]
|
||||
public async Task GetPage_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
var dtos = await InsertRange(Guid.NewGuid());
|
||||
var dtosCount = dtos.Count();
|
||||
var requestDto = new PaginationRequest()
|
||||
{
|
||||
Skip = 0,
|
||||
Take = 2,
|
||||
SortSettings = nameof(TechMessage.CategoryId)
|
||||
};
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetPage(requestDto, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.Equal(dtosCount, response.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InsertRange_returns_success()
|
||||
{
|
||||
await InsertRange(Guid.NewGuid());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InsertRange_returns_BadRequest()
|
||||
{
|
||||
//arrange
|
||||
const string exceptionMessage = "Ошибка валидации, формата или маршрутизации запроса";
|
||||
var systemId = Guid.NewGuid();
|
||||
var dtos = new List<TechMessageDto>()
|
||||
{
|
||||
new TechMessageDto()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
CategoryId = -1, // < 0
|
||||
Timestamp = DateTimeOffset.UtcNow,
|
||||
Text = string.Empty, // length < 0
|
||||
EventState = EventState.Triggered
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
//act
|
||||
var response = await techMessagesClient.AddRange(systemId, dtos, CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//assert
|
||||
Assert.Equal(exceptionMessage, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetSystems_returns_success()
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<DataSourceSystem>();
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetSystems(CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.Empty(response);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetSystems_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
await InsertRange(Guid.NewGuid());
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetSystems(CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
var expectedSystemCount = 1;
|
||||
Assert.Equal(expectedSystemCount, response!.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStatistics_returns_success()
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<DataSourceSystem>();
|
||||
|
||||
var categoryIds = new [] { 1, 2 };
|
||||
var systemIds = new [] { Guid.NewGuid() };
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetStatistics(systemIds, categoryIds, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.Empty(response);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStatistics_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
var categoryIds = new[] { 1 };
|
||||
var systemId = Guid.NewGuid();
|
||||
var dtos = await InsertRange(systemId);
|
||||
var filteredDtos = dtos.Where(e => categoryIds.Contains(e.CategoryId));
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetStatistics([systemId], categoryIds, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response);
|
||||
var categories = response
|
||||
.FirstOrDefault()!.Categories
|
||||
.Count();
|
||||
Assert.Equal(filteredDtos.Count(), categories);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDatesRange_returns_NoContent()
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<DataSourceSystem>();
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetDatesRangeAsync(CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.Null(response);
|
||||
}
|
||||
Assert.NotNull(response);
|
||||
Assert.Equal(dtosCount, response.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDatesRange_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
await InsertRange(Guid.NewGuid());
|
||||
[Fact]
|
||||
public async Task InsertRange_returns_success()
|
||||
{
|
||||
await InsertRange(Guid.NewGuid());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InsertRange_returns_BadRequest()
|
||||
{
|
||||
//arrange
|
||||
const string exceptionMessage = "Ошибка валидации, формата или маршрутизации запроса";
|
||||
var systemId = Guid.NewGuid();
|
||||
var dtos = new List<TechMessageDto>()
|
||||
{
|
||||
new TechMessageDto()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
CategoryId = -1, // < 0
|
||||
Timestamp = DateTimeOffset.UtcNow,
|
||||
Text = string.Empty, // length < 0
|
||||
EventState = EventState.Triggered
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
//act
|
||||
var response = await techMessagesClient.AddRange(systemId, dtos, CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//assert
|
||||
Assert.Equal(exceptionMessage, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetSystems_returns_success()
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<DataSourceSystem>();
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetSystems(CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.Empty(response);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetSystems_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
await InsertRange(Guid.NewGuid());
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetSystems(CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
var expectedSystemCount = 1;
|
||||
Assert.Equal(expectedSystemCount, response!.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStatistics_returns_success()
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<DataSourceSystem>();
|
||||
|
||||
var categoryIds = new[] { 1, 2 };
|
||||
var systemIds = new[] { Guid.NewGuid() };
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetStatistics(systemIds, categoryIds, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.Empty(response);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStatistics_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
var categoryIds = new[] { 1 };
|
||||
var systemId = Guid.NewGuid();
|
||||
var dtos = await InsertRange(systemId);
|
||||
var filteredDtos = dtos.Where(e => categoryIds.Contains(e.CategoryId));
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetStatistics([systemId], categoryIds, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response);
|
||||
var categories = response
|
||||
.FirstOrDefault()!.Categories
|
||||
.Count();
|
||||
Assert.Equal(filteredDtos.Count(), categories);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDatesRange_returns_NoContent()
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<DataSourceSystem>();
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetDatesRangeAsync(CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response?.From);
|
||||
Assert.NotNull(response?.To);
|
||||
}
|
||||
//assert
|
||||
Assert.Null(response);
|
||||
}
|
||||
|
||||
// [Fact]
|
||||
// public async Task GetPart_returns_success()
|
||||
// {
|
||||
// //arrange
|
||||
// var dateBegin = DateTimeOffset.UtcNow;
|
||||
// var take = 2;
|
||||
[Fact]
|
||||
public async Task GetDatesRange_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
await InsertRange(Guid.NewGuid());
|
||||
|
||||
// //act
|
||||
// var response = await techMessagesClient.GetPart(dateBegin, take, CancellationToken.None);
|
||||
//act
|
||||
var response = await techMessagesClient.GetDatesRangeAsync(CancellationToken.None);
|
||||
|
||||
// //assert
|
||||
// Assert.NotNull(response);
|
||||
// Assert.Empty(response);
|
||||
//}
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response?.From);
|
||||
Assert.NotNull(response?.To);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPart_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
var dateBegin = DateTimeOffset.UtcNow;
|
||||
var take = 1;
|
||||
await InsertRange(Guid.NewGuid());
|
||||
// [Fact]
|
||||
// public async Task GetPart_returns_success()
|
||||
// {
|
||||
// //arrange
|
||||
// var dateBegin = DateTimeOffset.UtcNow;
|
||||
// var take = 2;
|
||||
|
||||
// //act
|
||||
// var response = await techMessagesClient.GetPart(dateBegin, take, CancellationToken.None);
|
||||
|
||||
// //assert
|
||||
// Assert.NotNull(response);
|
||||
// Assert.Empty(response);
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPart_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
var dateBegin = DateTimeOffset.UtcNow;
|
||||
var take = 1;
|
||||
await InsertRange(Guid.NewGuid());
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetPart(dateBegin, take, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response);
|
||||
}
|
||||
//assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<TechMessageDto>> InsertRange(Guid systemId)
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<DataSourceSystem>();
|
||||
private async Task<IEnumerable<TechMessageDto>> InsertRange(Guid systemId)
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<DataSourceSystem>();
|
||||
|
||||
var dtos = new List<TechMessageDto>()
|
||||
{
|
||||
new TechMessageDto()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
CategoryId = 1,
|
||||
Timestamp = DateTimeOffset.UtcNow,
|
||||
Text = nameof(TechMessageDto.Text),
|
||||
EventState = Models.Enumerations.EventState.Triggered
|
||||
},
|
||||
new TechMessageDto()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
CategoryId = 2,
|
||||
Timestamp = DateTimeOffset.UtcNow,
|
||||
Text = nameof(TechMessageDto.Text),
|
||||
EventState = Models.Enumerations.EventState.Triggered
|
||||
}
|
||||
};
|
||||
var dtos = new List<TechMessageDto>()
|
||||
{
|
||||
new TechMessageDto()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
CategoryId = 1,
|
||||
Timestamp = DateTimeOffset.UtcNow,
|
||||
Text = nameof(TechMessageDto.Text),
|
||||
EventState = Models.Enumerations.EventState.Triggered
|
||||
},
|
||||
new TechMessageDto()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
CategoryId = 2,
|
||||
Timestamp = DateTimeOffset.UtcNow,
|
||||
Text = nameof(TechMessageDto.Text),
|
||||
EventState = Models.Enumerations.EventState.Triggered
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.AddRange(systemId, dtos, CancellationToken.None);
|
||||
//act
|
||||
var response = await techMessagesClient.AddRange(systemId, dtos, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.Equal(dtos.Count, response);
|
||||
//assert
|
||||
Assert.Equal(dtos.Count, response);
|
||||
|
||||
return dtos;
|
||||
}
|
||||
|
@ -1,16 +1,19 @@
|
||||
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 DD.Persistence.Client;
|
||||
using DD.Persistence.Client.Clients.Interfaces;
|
||||
using DD.Persistence.Database.Model;
|
||||
using System.Net;
|
||||
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, new()
|
||||
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||
{
|
||||
private readonly ITimeSeriesClient<TDto> timeSeriesClient;
|
||||
|
||||
@ -18,11 +21,12 @@ public abstract class TimeSeriesBaseControllerTest<TEntity, TDto> : BaseIntegrat
|
||||
{
|
||||
dbContext.CleanupDbSet<TEntity>();
|
||||
|
||||
var scope = factory.Services.CreateScope();
|
||||
var persistenceClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<PersistenceClientFactory>();
|
||||
var refitClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<IRefitClientFactory<IRefitTimeSeriesClient<TDto>>>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TimeSeriesClient<TDto>>>();
|
||||
|
||||
timeSeriesClient = persistenceClientFactory.GetTimeSeriesClient<TDto>();
|
||||
timeSeriesClient = scope.ServiceProvider
|
||||
.GetRequiredService<ITimeSeriesClient<TDto>>();
|
||||
}
|
||||
|
||||
public async Task InsertRangeSuccess(TDto dto)
|
||||
|
@ -3,6 +3,9 @@ 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
|
||||
@ -11,10 +14,12 @@ public class TimestampedSetControllerTest : BaseIntegrationTest
|
||||
|
||||
public TimestampedSetControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||
{
|
||||
var persistenceClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<PersistenceClientFactory>();
|
||||
var refitClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<IRefitClientFactory<IRefitTimestampedSetClient>>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TimestampedSetClient>>();
|
||||
|
||||
client = persistenceClientFactory.GetTimestampedSetClient();
|
||||
client = scope.ServiceProvider
|
||||
.GetRequiredService<ITimestampedSetClient>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1,10 +1,12 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
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 System.Net;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
using DD.Persistence.Client.Clients.Interfaces;
|
||||
using DD.Persistence.Client;
|
||||
|
||||
namespace DD.Persistence.IntegrationTests.Controllers;
|
||||
public class WitsDataControllerTest : BaseIntegrationTest
|
||||
@ -13,11 +15,12 @@ public class WitsDataControllerTest : BaseIntegrationTest
|
||||
|
||||
public WitsDataControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||
{
|
||||
var scope = factory.Services.CreateScope();
|
||||
var persistenceClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<PersistenceClientFactory>();
|
||||
var refitClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<IRefitClientFactory<IRefitWitsDataClient>>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<WitsDataClient>>();
|
||||
|
||||
witsDataClient = persistenceClientFactory.GetWitsDataClient();
|
||||
witsDataClient = scope.ServiceProvider
|
||||
.GetRequiredService<IWitsDataClient>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@ -10,14 +10,17 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.10" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="Refit" Version="8.0.0" />
|
||||
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -1,4 +1,7 @@
|
||||
namespace DD.Persistence.IntegrationTests
|
||||
using DD.Persistence.Client.Helpers;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace DD.Persistence.IntegrationTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Фабрика HTTP клиентов для интеграционных тестов
|
||||
@ -6,14 +9,19 @@
|
||||
public class TestHttpClientFactory : IHttpClientFactory
|
||||
{
|
||||
private readonly WebAppFactoryFixture factory;
|
||||
private readonly IConfiguration configuration;
|
||||
|
||||
public TestHttpClientFactory(WebAppFactoryFixture factory)
|
||||
public TestHttpClientFactory(WebAppFactoryFixture factory, IConfiguration configuration)
|
||||
{
|
||||
this.factory = factory;
|
||||
this.configuration = configuration;
|
||||
}
|
||||
public HttpClient CreateClient(string name)
|
||||
{
|
||||
return factory.CreateClient();
|
||||
var client = factory.CreateClient();
|
||||
client.Authorize(configuration);
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,9 @@ using DD.Persistence.Database.Model;
|
||||
using DD.Persistence.Database.Postgres;
|
||||
using RestSharp;
|
||||
using DD.Persistence.App;
|
||||
using DD.Persistence.Client.Helpers;
|
||||
using DD.Persistence.Factories;
|
||||
using System.Net;
|
||||
|
||||
namespace DD.Persistence.IntegrationTests;
|
||||
public class WebAppFactoryFixture : WebApplicationFactory<Program>
|
||||
@ -40,12 +43,12 @@ public class WebAppFactoryFixture : WebApplicationFactory<Program>
|
||||
services.AddLogging(builder => builder.AddConsole());
|
||||
|
||||
services.RemoveAll<IHttpClientFactory>();
|
||||
services.AddSingleton<IHttpClientFactory>(provider =>
|
||||
{
|
||||
return new TestHttpClientFactory(this);
|
||||
});
|
||||
|
||||
services.AddSingleton<PersistenceClientFactory>();
|
||||
services.AddSingleton<IHttpClientFactory>((provider) =>
|
||||
{
|
||||
return new TestHttpClientFactory(this, provider.GetRequiredService<IConfiguration>());
|
||||
});
|
||||
services.AddHttpClient();
|
||||
services.AddPersistenceClients();
|
||||
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
|
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>
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
@ -91,6 +91,7 @@ namespace DD.Persistence.Repository.Repositories
|
||||
await CreateSystemIfNotExist(systemId, token);
|
||||
|
||||
entity.SystemId = systemId;
|
||||
entity.Timestamp = dto.Timestamp.ToUniversalTime();
|
||||
|
||||
entities.Add(entity);
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<StartupObject>DD.Persistence.TestTelemetryStress.Program_v2</StartupObject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Program_v1.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Program_v1.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DD.Persistence.Database.Postgres\DD.Persistence.Database.Postgres.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,267 +0,0 @@
|
||||
|
||||
using DD.Persistence.Database.Entity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Npgsql.Internal;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
namespace DD.Persistence.TestTelemetryStress;
|
||||
|
||||
internal class Program_v1
|
||||
{
|
||||
record TestDataItem(TagValue[] TagValues, TagSetValue[] TagSetValue);
|
||||
enum Table { TagValue, TagSetValue }
|
||||
enum Action { insert, select }
|
||||
|
||||
record Log(
|
||||
Table Table,
|
||||
Action Action,
|
||||
DateTimeOffset Timestamp,
|
||||
TimeSpan TimeSpan,
|
||||
int tableLength,
|
||||
int count);
|
||||
|
||||
static Random random = new Random((int)(DateTimeOffset.UtcNow.Ticks % 0x7FFFFFFF));
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
/* Цель теста сравнить время отклика БД с телеметрией за все бурение
|
||||
* по 2м схемам хранения:
|
||||
* - TagSetValue - строка таблицы БД - 14 значений с одной отметкой времени.
|
||||
* - TagValue - строка таблицы БД - 1 значение с отметкой времени и id параметра, для записи одной телеметрии используется 14 строк.
|
||||
* Данные генерируются одинаковые и за все бурение - 30 дней *24 часа * 60 минут * 60 сек
|
||||
*
|
||||
* Вставка производится порциями по 56_000 записей для TagValue и по 4000 (56к/14) для TagSetValue.
|
||||
* Замеряется время каждой вставки для построения графика. Таблицы вставки чередуются.
|
||||
*
|
||||
* После вставки контекст уничтожается и делается пауза
|
||||
*
|
||||
* Создается новые контексты для тестирования каждой выборки по каждой таблице по отдельности.
|
||||
* Параметры выборок для тестирования:
|
||||
* - 10 раз за произвольные 10 минут, интервалы распределены по всему диапазону дат
|
||||
* - 10 раз за произвольные 60 минут, интервалы распределены по всему диапазону дат
|
||||
* - 10 раз за произвольные 24*60 минут, интервалы распределены по всему диапазону дат
|
||||
*/
|
||||
|
||||
var insertLogs = FillDB();
|
||||
Pause();
|
||||
var selectLogs = TestSelect();
|
||||
var logs = insertLogs.Union(selectLogs);
|
||||
ExportLogs(logs);
|
||||
/* Анализ логов:
|
||||
* - не замечено замедление вставки при увеличении размера таблиц
|
||||
* - размер таблиц в БД TagValue - 1200Мб TagSetValue - 119Мб. В 10 раз больше
|
||||
* - время вставки чанка TagValue в 7.3 раза больше времени вставки чанка TagSetValue
|
||||
* - время выборки из TagValue в 16,4 раза больше времени выборки из TagSetValue
|
||||
*/
|
||||
}
|
||||
|
||||
private static void ExportLogs(IEnumerable<Log> logs)
|
||||
{
|
||||
using var stream = File.CreateText("log.csv");
|
||||
foreach (Log log in logs)
|
||||
{
|
||||
stream.WriteLine($"{log.Table}, {log.Action}, {log.Timestamp}, {log.TimeSpan}, {log.count}, {log.tableLength}");
|
||||
}
|
||||
}
|
||||
|
||||
private static Log[] FillDB()
|
||||
{
|
||||
var begin = DateTimeOffset.UtcNow;
|
||||
var increment = TimeSpan.FromSeconds(1);
|
||||
var count = 30 * 24 * 60 * 60;
|
||||
var data = GenerateData(count, begin, increment);
|
||||
|
||||
using var db = GetDb();
|
||||
var tagValueSet = db.Set<TagValue>();
|
||||
var tagSetValueSet = db.Set<TagSetValue>();
|
||||
|
||||
var tagValueInsertedCount = 0;
|
||||
var tagSetValueInsertedCount = 0;
|
||||
var logs = new List<Log>(data.Length * 2);
|
||||
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
var chunk = data[i];
|
||||
var inserted = 0;
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
tagSetValueSet.AddRange(chunk.TagSetValue);
|
||||
inserted = db.SaveChanges();
|
||||
logs.Add(new Log(Table.TagSetValue, Action.insert, DateTimeOffset.UtcNow, stopwatch.Elapsed, tagSetValueInsertedCount, count));
|
||||
tagValueInsertedCount += inserted;
|
||||
db.ChangeTracker.Clear();
|
||||
|
||||
stopwatch.Restart();
|
||||
|
||||
tagValueSet.AddRange(chunk.TagValues);
|
||||
inserted = db.SaveChanges();
|
||||
logs.Add(new Log(Table.TagValue, Action.insert, DateTimeOffset.UtcNow, stopwatch.Elapsed, tagValueInsertedCount, count));
|
||||
tagSetValueInsertedCount += inserted;
|
||||
db.ChangeTracker.Clear();
|
||||
}
|
||||
|
||||
return logs.ToArray();
|
||||
}
|
||||
|
||||
private static void Pause()
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
private static Log[] TestSelect()
|
||||
{
|
||||
var testDateRanges = GetTestDateRanges();
|
||||
var logs1 = TestSelect<TagSetValue>(Table.TagSetValue, testDateRanges);
|
||||
var logs2 = TestSelect<TagValue>(Table.TagValue, testDateRanges);
|
||||
return [.. logs1, .. logs2];
|
||||
}
|
||||
|
||||
private static Log[] TestSelect<T>(Table table, (DateTimeOffset begin, DateTimeOffset end)[] ranges)
|
||||
where T : class, ITimestamped
|
||||
{
|
||||
using var db = GetDb();
|
||||
var dbSet = db.Set<T>();
|
||||
var totalCount = dbSet.Count();
|
||||
|
||||
var logs = new List<Log>(ranges.Length);
|
||||
|
||||
foreach (var range in ranges)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
var selected = dbSet
|
||||
.Where(e => e.Timestamp > range.begin)
|
||||
.Where(e => e.Timestamp < range.end)
|
||||
.ToArray();
|
||||
logs.Add(new(table, Action.select, DateTimeOffset.UtcNow, stopwatch.Elapsed, totalCount, selected.Length));
|
||||
}
|
||||
return logs.ToArray();
|
||||
}
|
||||
|
||||
private static (DateTimeOffset begin, DateTimeOffset end)[] GetTestDateRanges()
|
||||
{
|
||||
using var db = GetDb();
|
||||
|
||||
var dbSet1 = db.Set<TagSetValue>();
|
||||
var min1 = dbSet1.Min(e => e.Timestamp);
|
||||
var max1 = dbSet1.Max(e => e.Timestamp);
|
||||
|
||||
var dbSet2 = db.Set<TagValue>();
|
||||
var min2 = dbSet2.Min(e => e.Timestamp);
|
||||
var max2 = dbSet2.Max(e => e.Timestamp);
|
||||
|
||||
var min = min1 < min2 ? min1 : min2;
|
||||
var max = max1 < max2 ? max2 : max1;
|
||||
|
||||
var list1 = CalculateRanges(min, max, TimeSpan.FromMinutes(10), 10);
|
||||
var list2 = CalculateRanges(min, max, TimeSpan.FromMinutes(60), 10);
|
||||
var list3 = CalculateRanges(min, max, TimeSpan.FromMinutes(24 * 60), 10);
|
||||
return [..list1, ..list2, ..list3];
|
||||
}
|
||||
|
||||
private static (DateTimeOffset begin, DateTimeOffset end)[] CalculateRanges(DateTimeOffset min, DateTimeOffset max, TimeSpan range, int count)
|
||||
{
|
||||
if (max - min < range)
|
||||
throw new ArgumentException("max - min < range", nameof(range));
|
||||
|
||||
var result = new (DateTimeOffset begin, DateTimeOffset end)[count];
|
||||
var max1 = max - range;
|
||||
var delta = max1 - min;
|
||||
var step = delta / count;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var b = min + i * step;
|
||||
var e = b + step;
|
||||
result[i] = (b, e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static DbContext GetDb()
|
||||
{
|
||||
var factory = new Database.Postgres.DesignTimeDbContextFactory();
|
||||
var context = factory.CreateDbContext(Array.Empty<string>());
|
||||
context.Database.EnsureCreated();
|
||||
return context;
|
||||
}
|
||||
|
||||
private static TestDataItem[] GenerateData(int count, DateTimeOffset begin, TimeSpan increment)
|
||||
{
|
||||
var chunkLimit = 4000;
|
||||
var chunks = new List<TestDataItem>((count + chunkLimit) / chunkLimit);
|
||||
|
||||
for (int i = 0; i < count; i += chunkLimit)
|
||||
{
|
||||
var item = GenerateDataChunk(begin, increment, chunkLimit);
|
||||
chunks.Add(item);
|
||||
begin += increment * chunkLimit;
|
||||
}
|
||||
|
||||
return chunks.ToArray();
|
||||
}
|
||||
|
||||
private static TestDataItem GenerateDataChunk(DateTimeOffset begin, TimeSpan increment, int chunkLimit)
|
||||
{
|
||||
List<TagValue> tagValues = [];
|
||||
List<TagSetValue> tagSetValue = [];
|
||||
for (int i = 0; i < chunkLimit; i++)
|
||||
{
|
||||
var tagSet = GenerateTagSetValue(begin);
|
||||
tagSetValue.Add(tagSet);
|
||||
|
||||
var items = MakeTagValues(tagSet);
|
||||
tagValues.AddRange(items);
|
||||
begin += increment;
|
||||
}
|
||||
|
||||
return new(tagValues.ToArray(), tagSetValue.ToArray());
|
||||
}
|
||||
|
||||
private static TagSetValue GenerateTagSetValue(DateTimeOffset begin)
|
||||
{
|
||||
var result = new TagSetValue
|
||||
{
|
||||
Timestamp = begin,
|
||||
WellDepth = 100f * random.NextSingle(),
|
||||
BitDepth = 100f * random.NextSingle(),
|
||||
BlockPosition = 100f * random.NextSingle(),
|
||||
BlockSpeed = 100f * random.NextSingle(),
|
||||
Pressure = 100f * random.NextSingle(),
|
||||
AxialLoad = 100f * random.NextSingle(),
|
||||
HookWeight = 100f * random.NextSingle(),
|
||||
RotorTorque = 100f * random.NextSingle(),
|
||||
RotorSpeed = 100f * random.NextSingle(),
|
||||
Flow = 100f * random.NextSingle(),
|
||||
Mse = 100f * random.NextSingle(),
|
||||
Pump0Flow = 100f * random.NextSingle(),
|
||||
Pump1Flow = 100f * random.NextSingle(),
|
||||
Pump2Flow = 100f * random.NextSingle(),
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
private static TagValue[] MakeTagValues(TagSetValue tagSet)
|
||||
{
|
||||
TagValue[] data =
|
||||
[
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 1, Value = tagSet.WellDepth},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 2, Value = tagSet.BitDepth},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 3, Value = tagSet.BlockPosition},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 4, Value = tagSet.BlockSpeed},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 5, Value = tagSet.Pressure},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 6, Value = tagSet.AxialLoad},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 7, Value = tagSet.HookWeight},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 8, Value = tagSet.RotorTorque},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 9, Value = tagSet.RotorSpeed},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 10, Value = tagSet.Flow},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 11, Value = tagSet.Mse},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 12, Value = tagSet.Pump0Flow},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 13, Value = tagSet.Pump1Flow},
|
||||
new (){ Timestamp = tagSet.Timestamp, ParameterId = 14, Value = tagSet.Pump2Flow},
|
||||
];
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -1,354 +0,0 @@
|
||||
using DD.Persistence.Database.Entity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace DD.Persistence.TestTelemetryStress;
|
||||
|
||||
internal class Program_v2
|
||||
{
|
||||
record TestDataItem(TagBag[] TagBags, TagSetValue[] TagSetValue);
|
||||
enum Table { TagBag, TagSetValue }
|
||||
enum Action { insert, select }
|
||||
|
||||
record Log(
|
||||
Table Table,
|
||||
Action Action,
|
||||
DateTimeOffset Timestamp,
|
||||
TimeSpan TimeSpan,
|
||||
int tableLength,
|
||||
int count);
|
||||
|
||||
static Random random = new Random((int)(DateTimeOffset.UtcNow.Ticks % 0x7FFFFFFF));
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
/* Цель теста сравнить время отклика БД с телеметрией за все бурение
|
||||
* по 2м схемам хранения:
|
||||
* - TagSetValue - строка таблицы БД - 14 значений с одной отметкой времени.
|
||||
* - TagBag - строка таблицы БД - 1 значение с отметкой времени и id параметра, для записи одной телеметрии используется 14 строк.
|
||||
* Данные генерируются одинаковые и за все бурение - 30 дней *24 часа * 60 минут * 60 сек
|
||||
*
|
||||
* Вставка производится порциями по 172_800 записей (2 дня)
|
||||
* Замеряется время каждой вставки для построения графика. Таблицы вставки чередуются.
|
||||
*
|
||||
* После вставки контекст уничтожается и делается пауза
|
||||
*
|
||||
* Создается новые контексты для тестирования каждой выборки по каждой таблице по отдельности.
|
||||
* Параметры выборок для тестирования:
|
||||
* - 10 раз за произвольные 10 минут, интервалы распределены по всему диапазону дат
|
||||
* - 10 раз за произвольные 60 минут, интервалы распределены по всему диапазону дат
|
||||
* - 10 раз за произвольные 24*60 минут, интервалы распределены по всему диапазону дат
|
||||
*/
|
||||
var begin = DateTimeOffset.Now;
|
||||
Console.WriteLine($"Started at {begin}");
|
||||
var insertLogs = FillDB();
|
||||
Pause();
|
||||
var selectLogs = TestSelect();
|
||||
var logs = insertLogs.Union(selectLogs);
|
||||
|
||||
var end = DateTimeOffset.Now;
|
||||
Console.WriteLine($"complete at {end}, estimate {end - begin}");
|
||||
|
||||
AnalyzeAndExportLogs(logs);
|
||||
|
||||
/* Анализ логов:
|
||||
* - не замечено замедление вставки при увеличении размера таблиц 2592000 записей
|
||||
* - размер таблиц в БД TagBag - 778Мб TagSetValue - 285Мб. В 2,73 раз больше
|
||||
* - время вставки всех чанков TagBag - 2м11с, TagSetValue - 3м40с
|
||||
* - время выборки из TagBag 21.69c (256180 записей) на 65% медленнее времени выборки из TagSetValue 13,11с (256180 записей)
|
||||
* Вывод: приемлемо
|
||||
*/
|
||||
}
|
||||
|
||||
private static void AnalyzeAndExportLogs(IEnumerable<Log> logs)
|
||||
{
|
||||
var orderedLogs = logs
|
||||
.OrderBy(l => l.Action)
|
||||
.ThenBy(l => l.Table)
|
||||
.ThenBy(l => l.Timestamp);
|
||||
|
||||
var groups = orderedLogs
|
||||
.GroupBy(log => new { log.Action , log.Table })
|
||||
.OrderBy(group => group.Key.Action)
|
||||
.ThenBy(group => group.Key.Table);
|
||||
|
||||
using var analysisStream = File.CreateText($"Analysis_{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.csv");
|
||||
|
||||
analysisStream.WriteLine("{Table}, {Action}, Sum(Time), Sum(Count), Max(TableLength)");
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var max = group.Max(i => i.tableLength);
|
||||
var count = group.Sum(i => i.count);
|
||||
var timeMs = group.Sum(i => i.TimeSpan.TotalMilliseconds);
|
||||
var time = TimeSpan.FromMilliseconds(timeMs);
|
||||
var line = $"{group.Key.Table}, {group.Key.Action}, {time}, {count}, {max}";
|
||||
analysisStream.WriteLine(line);
|
||||
}
|
||||
|
||||
analysisStream.WriteLine(string.Empty);
|
||||
analysisStream.WriteLine("{Table}, {Action}, Avg(Time), Avg(Count), Max(TableLength)");
|
||||
|
||||
foreach (var group in groups.Where(g => g.Key.Action == Action.select))
|
||||
{
|
||||
var subGroups = group.GroupBy(i => Math.Round( 0.01d * i.count));
|
||||
|
||||
foreach (var subGroup in subGroups)
|
||||
{
|
||||
var max = subGroup.Max(i => i.tableLength);
|
||||
var count = subGroup.Average(i => i.count);
|
||||
var timeMs = subGroup.Average(i => i.TimeSpan.TotalMilliseconds);
|
||||
var time = TimeSpan.FromMilliseconds(timeMs);
|
||||
var line = $"{group.Key.Table}, {group.Key.Action}, {time}, {count}, {max}";
|
||||
analysisStream.WriteLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
analysisStream.WriteLine(string.Empty);
|
||||
analysisStream.WriteLine("Table, Action, Time, Count, TableLength");
|
||||
|
||||
foreach (var log in orderedLogs)
|
||||
{
|
||||
analysisStream.WriteLine($"{log.Table}, {log.Action}, {log.TimeSpan}, {log.count}, {log.tableLength}");
|
||||
}
|
||||
}
|
||||
|
||||
private static Log[] FillDB()
|
||||
{
|
||||
var begin = DateTimeOffset.UtcNow;
|
||||
var increment = TimeSpan.FromSeconds(1);
|
||||
var count = 30 * 24 * 60 * 60;
|
||||
var logStopwatch = Stopwatch.StartNew();
|
||||
var data = GenerateData(count, begin, increment);
|
||||
Console.WriteLine($"Data[{data.Length}] generated {logStopwatch.Elapsed}");
|
||||
|
||||
logStopwatch.Restart();
|
||||
using var db = GetDb();
|
||||
db.Database.EnsureDeleted();
|
||||
db.Database.EnsureCreated();
|
||||
Console.WriteLine($"Database recreated {logStopwatch.Elapsed}");
|
||||
|
||||
|
||||
var tagBagSet = db.Set<TagBag>();
|
||||
var tagSetValueSet = db.Set<TagSetValue>();
|
||||
|
||||
var tagBagInsertedCount = 0;
|
||||
var tagSetValueInsertedCount = 0;
|
||||
var logs = new List<Log>(data.Length * 2);
|
||||
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
var chunk = data[i];
|
||||
var inserted = 0;
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
tagSetValueSet.AddRange(chunk.TagSetValue);
|
||||
inserted = db.SaveChanges();
|
||||
logs.Add(new Log(Table.TagSetValue, Action.insert, DateTimeOffset.UtcNow, stopwatch.Elapsed, tagBagInsertedCount, inserted));
|
||||
tagBagInsertedCount += inserted;
|
||||
db.ChangeTracker.Clear();
|
||||
|
||||
Console.WriteLine($"chunk {i} of {data.Length} TagSetValue[{chunk.TagSetValue.Length}] added {stopwatch.Elapsed}");
|
||||
stopwatch.Restart();
|
||||
|
||||
tagBagSet.AddRange(chunk.TagBags);
|
||||
inserted = db.SaveChanges();
|
||||
logs.Add(new Log(Table.TagBag, Action.insert, DateTimeOffset.UtcNow, stopwatch.Elapsed, tagSetValueInsertedCount, inserted));
|
||||
tagSetValueInsertedCount += inserted;
|
||||
db.ChangeTracker.Clear();
|
||||
|
||||
Console.WriteLine($"chunk {i} of {data.Length} TagBags[{chunk.TagBags.Length}] added {stopwatch.Elapsed}");
|
||||
}
|
||||
|
||||
return logs.ToArray();
|
||||
}
|
||||
|
||||
private static void Pause()
|
||||
{
|
||||
GC.Collect();
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
private static Log[] TestSelect()
|
||||
{
|
||||
var testDateRanges = GetTestDateRanges();
|
||||
var logs1 = TestSelect<TagSetValue>(Table.TagSetValue, testDateRanges);
|
||||
var logs2 = TestSelect<TagBag>(Table.TagBag, testDateRanges);
|
||||
return [.. logs1, .. logs2];
|
||||
}
|
||||
|
||||
private static Log[] TestSelect<T>(Table table, (DateTimeOffset begin, DateTimeOffset end)[] ranges)
|
||||
where T : class, ITimestamped
|
||||
{
|
||||
using var db = GetDb();
|
||||
var dbSet = db.Set<T>();
|
||||
var totalCount = dbSet.Count();
|
||||
|
||||
var logs = new List<Log>(ranges.Length);
|
||||
|
||||
foreach (var range in ranges)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
var selected = dbSet
|
||||
.Where(e => e.Timestamp > range.begin)
|
||||
.Where(e => e.Timestamp < range.end)
|
||||
.ToArray();
|
||||
var count = selected.Length;
|
||||
var maxTime = selected.Max(e => e.Timestamp);
|
||||
logs.Add(new Log(table, Action.select, DateTimeOffset.UtcNow, stopwatch.Elapsed, totalCount, count));
|
||||
}
|
||||
return logs.ToArray();
|
||||
}
|
||||
|
||||
private static (DateTimeOffset begin, DateTimeOffset end)[] GetTestDateRanges()
|
||||
{
|
||||
using var db = GetDb();
|
||||
|
||||
var dbSet1 = db.Set<TagSetValue>();
|
||||
var min1 = dbSet1.Min(e => e.Timestamp);
|
||||
var max1 = dbSet1.Max(e => e.Timestamp);
|
||||
|
||||
var dbSet2 = db.Set<TagBag>();
|
||||
var min2 = dbSet2.Min(e => e.Timestamp);
|
||||
var max2 = dbSet2.Max(e => e.Timestamp);
|
||||
|
||||
var min = min1 < min2 ? min1 : min2;
|
||||
var max = max1 > max2 ? max1 : max2;
|
||||
|
||||
var list1 = CalculateRanges(min, max, TimeSpan.FromMinutes(60), 10);
|
||||
var list2 = CalculateRanges(min, max, TimeSpan.FromMinutes(12 * 60), 10);
|
||||
var list3 = CalculateRanges(min, max, TimeSpan.FromMinutes(24 * 60), 10);
|
||||
|
||||
return [..list1, ..list2, ..list3];
|
||||
}
|
||||
|
||||
private static (DateTimeOffset begin, DateTimeOffset end)[] CalculateRanges(DateTimeOffset min, DateTimeOffset max, TimeSpan range, int count)
|
||||
{
|
||||
if (max - min < range)
|
||||
throw new ArgumentException("max - min < range", nameof(range));
|
||||
|
||||
var result = new (DateTimeOffset begin, DateTimeOffset end)[count];
|
||||
var max1 = max - range;
|
||||
var delta = max1 - min;
|
||||
var step = delta / count;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var b = min + i * step;
|
||||
var e = b + range;
|
||||
result[i] = (b, e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static DbContext GetDb()
|
||||
{
|
||||
var factory = new Database.Postgres.DesignTimeDbContextFactory();
|
||||
var context = factory.CreateDbContext(Array.Empty<string>());
|
||||
return context;
|
||||
}
|
||||
|
||||
private static TestDataItem[] GenerateData(int count, DateTimeOffset begin, TimeSpan increment)
|
||||
{
|
||||
var chunkLimit = 172_800;
|
||||
var chunks = new List<TestDataItem>((count + chunkLimit) / chunkLimit);
|
||||
|
||||
for (int i = 0; i < count; i += chunkLimit)
|
||||
{
|
||||
var item = GenerateDataChunk(begin, increment, chunkLimit);
|
||||
chunks.Add(item);
|
||||
begin += increment * chunkLimit;
|
||||
}
|
||||
|
||||
return chunks.ToArray();
|
||||
}
|
||||
|
||||
private static TestDataItem GenerateDataChunk(DateTimeOffset begin, TimeSpan increment, int chunkLimit)
|
||||
{
|
||||
List<TagBag> tagBags = [];
|
||||
List<TagSetValue> tagSetValue = [];
|
||||
for (int i = 0; i < chunkLimit; i++)
|
||||
{
|
||||
var tagSet = GenerateTagSetValue(begin);
|
||||
tagSetValue.Add(tagSet);
|
||||
|
||||
var tagBag = MakeTagBag(tagSet);
|
||||
tagBags.Add(tagBag);
|
||||
begin += increment;
|
||||
}
|
||||
|
||||
return new(tagBags.ToArray(), tagSetValue.ToArray());
|
||||
}
|
||||
|
||||
private static TagSetValue GenerateTagSetValue(DateTimeOffset begin)
|
||||
{
|
||||
var result = new TagSetValue
|
||||
{
|
||||
Timestamp = begin,
|
||||
WellDepth = 100f * random.NextSingle(),
|
||||
BitDepth = 100f * random.NextSingle(),
|
||||
BlockPosition = 100f * random.NextSingle(),
|
||||
BlockSpeed = 100f * random.NextSingle(),
|
||||
Pressure = 100f * random.NextSingle(),
|
||||
AxialLoad = 100f * random.NextSingle(),
|
||||
HookWeight = 100f * random.NextSingle(),
|
||||
RotorTorque = 100f * random.NextSingle(),
|
||||
RotorSpeed = 100f * random.NextSingle(),
|
||||
Flow = 100f * random.NextSingle(),
|
||||
Mse = random.Next(),
|
||||
Mode = random.Next(),
|
||||
Pump0Flow = 100f * random.NextSingle(),
|
||||
Pump1Flow = 100f * random.NextSingle(),
|
||||
Pump2Flow = 100f * random.NextSingle(),
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Guid BagId = Guid.NewGuid();
|
||||
private static TagBag MakeTagBag(TagSetValue tagSet)
|
||||
{
|
||||
TagBag data = new()
|
||||
{
|
||||
BagId = BagId,
|
||||
Timestamp = tagSet.Timestamp,
|
||||
Values =
|
||||
[
|
||||
tagSet.WellDepth,
|
||||
tagSet.BitDepth,
|
||||
tagSet.BlockPosition,
|
||||
tagSet.BlockSpeed,
|
||||
tagSet.Pressure,
|
||||
tagSet.AxialLoad,
|
||||
tagSet.HookWeight,
|
||||
tagSet.RotorTorque,
|
||||
tagSet.RotorSpeed,
|
||||
tagSet.Flow,
|
||||
tagSet.Mse,
|
||||
tagSet.Mode,
|
||||
tagSet.Pump0Flow,
|
||||
tagSet.Pump1Flow,
|
||||
tagSet.Pump2Flow,
|
||||
],
|
||||
//new Dictionary<string, object>()
|
||||
//{
|
||||
// {"WellDepth", tagSet.WellDepth },
|
||||
// {"BitDepth", tagSet.BitDepth},
|
||||
// {"BlockPosition", tagSet.BlockPosition},
|
||||
// {"BlockSpeed", tagSet.BlockSpeed},
|
||||
// {"Pressure", tagSet.Pressure},
|
||||
// {"AxialLoad", tagSet.AxialLoad},
|
||||
// {"HookWeight", tagSet.HookWeight},
|
||||
// {"RotorTorque", tagSet.RotorTorque},
|
||||
// {"RotorSpeed", tagSet.RotorSpeed},
|
||||
// {"Flow", tagSet.Flow},
|
||||
// {"Mse", tagSet.Mse},
|
||||
// {"Pump0Flow", tagSet.Pump0Flow},
|
||||
// {"Pump1Flow", tagSet.Pump1Flow},
|
||||
// {"Pump2Flow", tagSet.Pump2Flow},
|
||||
//}
|
||||
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
## Dictionary<string, object>
|
||||
|
||||
TagBag insert 00:02:31,55 2592000 2419200
|
||||
TagSetValue insert 00:04:14,89 2592000 2419200
|
||||
TagBag select 00:00:16,91 905997 2592000
|
||||
TagSetValue select 00:00:02,42 905997 2592000
|
||||
|
||||
sizeof(TagBag) = 1200M
|
||||
sizeof(TagSetValue) = 285M
|
||||
Tot = 7m
|
||||
|
||||
## object[]
|
||||
|
||||
{Table} {Action} Sum(Time) Sum(Count) Max(TableLength)
|
||||
TagBag insert 00:02:13,39 2592000 2419200
|
||||
TagSetValue insert 00:03:57,70 2592000 2419200
|
||||
TagBag select 00:00:12,60 1331997 2592000
|
||||
TagSetValue select 00:00:02,88 1331997 2592000
|
||||
|
||||
sizeof(TagBag) = 805M
|
||||
sizeof(TagSetValue) = 305M
|
||||
|
||||
## multi rows
|
||||
размер таблиц в БД TagValue - 1200Мб TagSetValue - 119Мб. В 10 раз больше
|
||||
время вставки чанка TagValue в 7.3 раза больше времени вставки чанка TagSetValue
|
||||
время выборки из TagValue в 16,4 раза больше времени выборки из TagSetValue
|
@ -19,7 +19,7 @@ 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.TestTelemetryStress", "DD.Persistence.TestTelemetryStress\DD.Persistence.TestTelemetryStress.csproj", "{A75B3712-1E1D-4C5B-B97C-72C80E7D6BDE}"
|
||||
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
|
||||
@ -59,10 +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
|
||||
{A75B3712-1E1D-4C5B-B97C-72C80E7D6BDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A75B3712-1E1D-4C5B-B97C-72C80E7D6BDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A75B3712-1E1D-4C5B-B97C-72C80E7D6BDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A75B3712-1E1D-4C5B-B97C-72C80E7D6BDE}.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,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
@ -17,8 +17,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.2.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DD.Persistence.Models\DD.Persistence.Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -19,6 +19,7 @@ namespace DD.Persistence.Repositories
|
||||
/// <summary>
|
||||
/// Добавление новых сообщений
|
||||
/// </summary>
|
||||
/// <param name="systemId"></param>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
|
Loading…
Reference in New Issue
Block a user