diff --git a/DD.Persistence.API/Controllers/SetpointController.cs b/DD.Persistence.API/Controllers/SetpointController.cs
index 663a1c4..2f1c6fc 100644
--- a/DD.Persistence.API/Controllers/SetpointController.cs
+++ b/DD.Persistence.API/Controllers/SetpointController.cs
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc;
using DD.Persistence.Models;
using DD.Persistence.Repositories;
using System.Net;
+using System.Text.Json;
using DD.Persistence.Models.Common;
namespace DD.Persistence.API.Controllers;
@@ -29,9 +30,9 @@ public class SetpointController : ControllerBase, ISetpointApi
///
///
[HttpGet("current")]
- public async Task>> GetCurrent([FromQuery] IEnumerable setpointKeys, CancellationToken token)
+ public async Task>> GetCurrent([FromQuery] IEnumerable setpointKeys, CancellationToken token)
{
- var result = await setpointRepository.GetCurrent(setpointKeys, token);
+ var result = await setpointRepository.GetCurrentDictionary(setpointKeys, token);
return Ok(result);
}
@@ -105,7 +106,7 @@ public class SetpointController : ControllerBase, ISetpointApi
public async Task Add(Guid setpointKey, object newValue, CancellationToken token)
{
var userId = User.GetUserId();
- await setpointRepository.Add(setpointKey, newValue, userId, token);
+ await setpointRepository.Add(setpointKey, (JsonElement)newValue, userId, token);
return CreatedAtAction(nameof(Add), true);
}
diff --git a/DD.Persistence.API/Readme.md b/DD.Persistence.API/Readme.md
new file mode 100644
index 0000000..43bbbf3
--- /dev/null
+++ b/DD.Persistence.API/Readme.md
@@ -0,0 +1,51 @@
+# Persistence Service Readme
+
+## Краткое описание
+Persistence сервис отвечает за работу с хранимыми данными
+в рамках совокупности различных систем.
+
+## Локальное развертывание
+1. Скачать репозиторий по SSH
+```
+ssh://git@git.ddrilling.ru:2221/on.nemtina/persistence.git
+```
+
+Для доступа к репозиториям редварительно необходимо сгенерировать SSH ключ и добавить его в Gitea
+
+2. Выбрать ветку dev
+
+## Использование Swagger-а
+1. Сконфигурировать appsettings.Development.json
+(при отсутствии) занести флаг:
+```json
+"NeedUseKeyCloak": true
+```
+2. Запустить решение в режиме Debug
+3. Выполнить авторизацию через KeyCloak - качестве client_id указать:
+```
+webapi
+```
+После этого должен произойти редирект на страницу авторизации в KeyCloak
+
+4. Заполнить поля и авторизоваться
+```
+Username or email: myuser
+```
+```
+Password: 12345
+```
+
+## Тестирование
+Запуск тестов рекомендуется осуществлять без использования KeyCloak
Для этого
+настройка appsettings.Tests.json должна содержать:
+```
+"NeedUseKeyCloak": false,
+"AuthUser": {
+ "username": "myuser",
+ "password": 12345,
+ "clientId": "webapi",
+ "grantType": "password"
+}
+```
+
+
diff --git a/DD.Persistence.Client/Clients/Interfaces/ISetpointClient.cs b/DD.Persistence.Client/Clients/Interfaces/ISetpointClient.cs
index 5745e7e..407eb1c 100644
--- a/DD.Persistence.Client/Clients/Interfaces/ISetpointClient.cs
+++ b/DD.Persistence.Client/Clients/Interfaces/ISetpointClient.cs
@@ -25,12 +25,21 @@ public interface ISetpointClient : IDisposable
///
Task> GetCurrent(IEnumerable setpointKeys, CancellationToken token);
- ///
- /// Получить диапазон дат, для которых есть данные в репозитории
- ///
- ///
- ///
- Task GetDatesRangeAsync(CancellationToken token);
+ ///
+ /// Получить актуальные значения уставок
+ ///
+ ///
+ ///
+ /// s
+ Task> GetCurrentDictionary(IEnumerable setpointConfigs, CancellationToken token);
+
+
+ ///
+ /// Получить диапазон дат, для которых есть данные в репозитории
+ ///
+ ///
+ ///
+ Task GetDatesRangeAsync(CancellationToken token);
///
/// Получить значения уставок за определенный момент времени
diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitSetpointClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitSetpointClient.cs
index 8f75dfa..7931e6d 100644
--- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitSetpointClient.cs
+++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitSetpointClient.cs
@@ -1,6 +1,7 @@
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using Refit;
+using System.Text.Json;
namespace DD.Persistence.Client.Clients.Interfaces.Refit;
@@ -8,8 +9,11 @@ public interface IRefitSetpointClient : IRefitClient, IDisposable
{
private const string BaseRoute = "/api/setpoint";
+ //[Get($"{BaseRoute}/current")]
+ //Task>> GetCurrent([Query(CollectionFormat.Multi)] IEnumerable setpointKeys, CancellationToken token);
+
[Get($"{BaseRoute}/current")]
- Task>> GetCurrent([Query(CollectionFormat.Multi)] IEnumerable setpointKeys, CancellationToken token);
+ Task>> GetCurrent([Query(CollectionFormat.Multi)] IEnumerable setpointKeys, CancellationToken token);
[Get($"{BaseRoute}/history")]
Task>> GetHistory([Query(CollectionFormat.Multi)] IEnumerable setpointKeys, [Query] DateTimeOffset historyMoment, CancellationToken token);
diff --git a/DD.Persistence.Client/Clients/SetpointClient.cs b/DD.Persistence.Client/Clients/SetpointClient.cs
index c43a0b2..dfb5026 100644
--- a/DD.Persistence.Client/Clients/SetpointClient.cs
+++ b/DD.Persistence.Client/Clients/SetpointClient.cs
@@ -1,8 +1,11 @@
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging;
using DD.Persistence.Client.Clients.Base;
using DD.Persistence.Client.Clients.Interfaces;
using DD.Persistence.Client.Clients.Interfaces.Refit;
using DD.Persistence.Models;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Globalization;
using DD.Persistence.Models.Common;
namespace DD.Persistence.Client.Clients;
@@ -10,25 +13,48 @@ namespace DD.Persistence.Client.Clients;
public class SetpointClient : BaseClient, ISetpointClient
{
private readonly IRefitSetpointClient refitSetpointClient;
+ private readonly ISetpointConfigStorage setpointConfigStorage;
- public SetpointClient(IRefitClientFactory refitSetpointClientFactory, ILogger logger) : base(logger)
+ public SetpointClient(
+ IRefitClientFactory refitSetpointClientFactory,
+ ISetpointConfigStorage setpointConfigStorage,
+ ILogger logger) : base(logger)
{
this.refitSetpointClient = refitSetpointClientFactory.Create();
- }
+ this.setpointConfigStorage = setpointConfigStorage;
+ }
public async Task> GetCurrent(IEnumerable setpointKeys, CancellationToken token)
{
var result = await ExecuteGetResponse(
async () => await refitSetpointClient.GetCurrent(setpointKeys, token), token);
- return result!;
+ return result!.Select(x => new SetpointValueDto {
+ Key = x.Key,
+ Value = DeserializeValue(x.Key, x.Value)
+ });
}
- public async Task> GetHistory(IEnumerable setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
+
+
+ public async Task> GetCurrentDictionary(IEnumerable setpointConfigs, CancellationToken token)
+ {
+ var result = await ExecuteGetResponse(
+ async () => await refitSetpointClient.GetCurrent(setpointConfigs, token), token);
+
+
+ return result!.ToDictionary(x => x.Key,x => DeserializeValue(x.Key,x.Value));
+ }
+
+ public async Task> GetHistory(IEnumerable setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
{
var result = await ExecuteGetResponse(
async () => await refitSetpointClient.GetHistory(setpointKeys, historyMoment, token), token);
+ foreach(var dto in result)
+ dto.Value = DeserializeValue(dto.Key, (JsonElement)dto.Value);
+
+
return result!;
}
@@ -37,6 +63,9 @@ public class SetpointClient : BaseClient, ISetpointClient
var result = await ExecuteGetResponse(
async () => await refitSetpointClient.GetLog(setpointKeys, token), token);
+ foreach(var item in result)
+ DeserializeList(result[item.Key]);
+
return result!;
}
@@ -49,14 +78,18 @@ public class SetpointClient : BaseClient, ISetpointClient
}
public async Task> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token)
- {
- var result = await ExecuteGetResponse(
- async () => await refitSetpointClient.GetPart(dateBegin, take, token), token);
+ {
+ var result = await ExecuteGetResponse(
+ async () => await refitSetpointClient.GetPart(dateBegin, take, token), token);
- return result!;
- }
+ DeserializeList(result);
- public async Task Add(Guid setpointKey, object newValue, CancellationToken token)
+ return result!;
+ }
+
+
+
+ public async Task Add(Guid setpointKey, object newValue, CancellationToken token)
{
await ExecutePostResponse(
async () => await refitSetpointClient.Add(setpointKey, newValue, token), token);
@@ -68,4 +101,21 @@ public class SetpointClient : BaseClient, ISetpointClient
GC.SuppressFinalize(this);
}
+
+
+ private object DeserializeValue(Guid key, JsonElement value)
+ {
+ if (setpointConfigStorage.TryGetType(key, out var type))
+ return value.Deserialize(type)!;
+
+ return value;
+ }
+ private void DeserializeList(IEnumerable? result)
+ {
+ foreach (var log in result)
+ log.Value = DeserializeValue(log.Key, (JsonElement)log.Value);
+
+ }
+
+
}
diff --git a/DD.Persistence.Client/DD.Persistence.Client.csproj b/DD.Persistence.Client/DD.Persistence.Client.csproj
index 12bdea4..3491596 100644
--- a/DD.Persistence.Client/DD.Persistence.Client.csproj
+++ b/DD.Persistence.Client/DD.Persistence.Client.csproj
@@ -11,9 +11,9 @@
DD.Persistence.Client
- 1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))
+ 1.4.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)).1
- 1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))
+ 1.4.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)).1
DD.Persistence.Client
@@ -33,15 +33,15 @@
snupkg
- C:\Projects\Nuget\Persistence\Client
+ C:\Projects\Nuget\Persistence
Readme.md
- 1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))
- 1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))
+ 1.4.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))
+ 1.4.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))
diff --git a/DD.Persistence.Client/DependencyInjection.cs b/DD.Persistence.Client/DependencyInjection.cs
index 422cf8f..89f65cf 100644
--- a/DD.Persistence.Client/DependencyInjection.cs
+++ b/DD.Persistence.Client/DependencyInjection.cs
@@ -15,7 +15,7 @@ public static class DependencyInjection
///
///
///
- public static IServiceCollection AddPersistenceClients(this IServiceCollection services)
+ public static IServiceCollection AddPersistenceClients(this IServiceCollection services, Dictionary? setpointTypeConfigs = null)
{
services.AddTransient(typeof(IRefitClientFactory<>), typeof(RefitClientFactory<>));
services.AddTransient();
@@ -24,6 +24,11 @@ public static class DependencyInjection
services.AddTransient();
services.AddTransient();
services.AddTransient();
+
+ services.AddSingleton(provider =>
+ {
+ return new SetpointConfigStorage(setpointTypeConfigs);
+ });
return services;
}
}
diff --git a/DD.Persistence.Client/ISetpointConfigStorage.cs b/DD.Persistence.Client/ISetpointConfigStorage.cs
new file mode 100644
index 0000000..2c73783
--- /dev/null
+++ b/DD.Persistence.Client/ISetpointConfigStorage.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DD.Persistence.Client;
+public interface ISetpointConfigStorage
+{
+ bool TryGetType(Guid id, out Type type);
+}
diff --git a/DD.Persistence.Client/SetpointConfigStorage.cs b/DD.Persistence.Client/SetpointConfigStorage.cs
new file mode 100644
index 0000000..5cfbabf
--- /dev/null
+++ b/DD.Persistence.Client/SetpointConfigStorage.cs
@@ -0,0 +1,20 @@
+namespace DD.Persistence.Client;
+internal class SetpointConfigStorage : ISetpointConfigStorage
+{
+ private readonly Dictionary setpointTypeConfigs;
+
+ public SetpointConfigStorage(Dictionary? setpointTypeConfigs)
+ {
+ this.setpointTypeConfigs = setpointTypeConfigs?? new Dictionary();
+ }
+
+ public bool TryGetType(Guid id, out Type type)
+ {
+ return setpointTypeConfigs.TryGetValue(id, out type);
+ }
+
+ public void AddOrReplace(Guid id, Type type)
+ {
+ setpointTypeConfigs[id] = type;
+ }
+}
diff --git a/DD.Persistence.Database/Entity/Setpoint.cs b/DD.Persistence.Database/Entity/Setpoint.cs
index e3c2ef5..e526c22 100644
--- a/DD.Persistence.Database/Entity/Setpoint.cs
+++ b/DD.Persistence.Database/Entity/Setpoint.cs
@@ -1,6 +1,7 @@
using DD.Persistence.Database.EntityAbstractions;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
+using System.Text.Json;
namespace DD.Persistence.Database.Model
{
@@ -11,7 +12,7 @@ namespace DD.Persistence.Database.Model
public Guid Key { get; set; }
[Column(TypeName = "jsonb"), Comment("Значение уставки")]
- public required object Value { get; set; }
+ public required JsonElement Value { get; set; }
[Comment("Дата создания уставки")]
public DateTimeOffset Timestamp { get; set; }
diff --git a/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs
index 5b36d6a..e84402a 100644
--- a/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs
+++ b/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs
@@ -5,6 +5,7 @@ using DD.Persistence.Client.Clients.Interfaces.Refit;
using DD.Persistence.Database.Model;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using System.Text.Json;
using Xunit;
namespace DD.Persistence.IntegrationTests.Controllers
@@ -12,6 +13,7 @@ namespace DD.Persistence.IntegrationTests.Controllers
public class SetpointControllerTest : BaseIntegrationTest
{
private readonly ISetpointClient setpointClient;
+ private readonly SetpointConfigStorage configStorage;
public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory)
{
var refitClientFactory = scope.ServiceProvider
@@ -20,8 +22,36 @@ namespace DD.Persistence.IntegrationTests.Controllers
setpointClient = scope.ServiceProvider
.GetRequiredService();
+
+ configStorage = (SetpointConfigStorage)scope.ServiceProvider.GetRequiredService();
}
+
+ [Fact]
+ public async Task GetCurrent_returns_correctType()
+ {
+ var id = Guid.Parse("e0fcad22-1761-476e-a729-a3c59d51ba41");
+
+ configStorage.AddOrReplace(id, typeof(float));
+
+ await setpointClient.Add(id, 48.3f, CancellationToken.None);
+
+ //act
+ var response = await setpointClient.GetCurrent([id], CancellationToken.None);
+
+ //assert
+ Assert.NotNull(response);
+ Assert.NotEmpty(response);
+ Assert.Single(response);
+ var item = response.First();
+ Assert.Equal(item.Key, id);
+
+ Assert.IsNotType(item.Value);
+ Assert.Equal(item.Value, 48.3f);
+ }
+
+
+
[Fact]
public async Task GetCurrent_returns_success()
{
@@ -33,7 +63,7 @@ namespace DD.Persistence.IntegrationTests.Controllers
};
//act
- var response = await setpointClient.GetCurrent(setpointKeys, new CancellationToken());
+ var response = await setpointClient.GetCurrent(setpointKeys, CancellationToken.None);
//assert
Assert.NotNull(response);
diff --git a/DD.Persistence.Models/DD.Persistence.Models.csproj b/DD.Persistence.Models/DD.Persistence.Models.csproj
index dc4772e..6bcae66 100644
--- a/DD.Persistence.Models/DD.Persistence.Models.csproj
+++ b/DD.Persistence.Models/DD.Persistence.Models.csproj
@@ -11,9 +11,9 @@
DD.Persistence.Models
- 1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))
+ 1.2.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))
- 1.0.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))
+ 1.2.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH))
DD.Persistence.Models
@@ -33,7 +33,7 @@
snupkg
- C:\Projects\Nuget\Persistence\Models
+ C:\Projects\Nuget\Persistence
diff --git a/DD.Persistence.Repository.Test/DD.Persistence.Repository.Test.csproj b/DD.Persistence.Repository.Test/DD.Persistence.Repository.Test.csproj
new file mode 100644
index 0000000..4f29d86
--- /dev/null
+++ b/DD.Persistence.Repository.Test/DD.Persistence.Repository.Test.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net9.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DD.Persistence.Repository.Test/RepositoryTestFixture.cs b/DD.Persistence.Repository.Test/RepositoryTestFixture.cs
new file mode 100644
index 0000000..81e3694
--- /dev/null
+++ b/DD.Persistence.Repository.Test/RepositoryTestFixture.cs
@@ -0,0 +1,32 @@
+
+using DD.Persistence.Database;
+using DD.Persistence.Database.Model;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Caching.Memory;
+using Testcontainers.PostgreSql;
+using Xunit;
+
+namespace DD.Persistence.Repository.Test;
+
+public class RepositoryTestFixture : IAsyncLifetime
+{
+ public readonly PostgreSqlContainer dbContainer = new PostgreSqlBuilder().Build();
+
+
+ public PersistencePostgresContext GetDbContext() => new(new DbContextOptionsBuilder()
+ .UseNpgsql(dbContainer.GetConnectionString()).Options);
+
+ public IMemoryCache GetMemoryCache() => new MemoryCache(new MemoryCacheOptions());
+
+ public virtual async Task InitializeAsync()
+ {
+ await dbContainer.StartAsync();
+ var forumDbContext = new PersistencePostgresContext(new DbContextOptionsBuilder()
+ .UseNpgsql(dbContainer.GetConnectionString()).Options);
+
+ await forumDbContext.Database.MigrateAsync();
+ }
+
+ public async Task DisposeAsync() => await dbContainer.DisposeAsync();
+}
+
diff --git a/DD.Persistence.Repository.Test/SetpointRepositoryShould.cs b/DD.Persistence.Repository.Test/SetpointRepositoryShould.cs
new file mode 100644
index 0000000..6b05ff3
--- /dev/null
+++ b/DD.Persistence.Repository.Test/SetpointRepositoryShould.cs
@@ -0,0 +1,56 @@
+using DD.Persistence.Database.Model;
+using DD.Persistence.Repository.Repositories;
+using Shouldly;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace DD.Persistence.Repository.Test;
+public class SetpointRepositoryShould : IClassFixture
+{
+ private readonly RepositoryTestFixture fixture;
+ private readonly PersistencePostgresContext context;
+ private readonly SetpointRepository sut;
+
+ public SetpointRepositoryShould(RepositoryTestFixture fixture)
+ {
+ this.fixture = fixture;
+ context = fixture.GetDbContext();
+ sut = new SetpointRepository(context);
+ }
+
+ [Fact]
+ public async Task ReturnValueKindNumber()
+ {
+ var id = Guid.NewGuid();
+ var value = GetJsonFromObject(22);
+ await sut.Add(id, value, Guid.NewGuid(), CancellationToken.None);
+
+ var t = fixture.dbContainer.GetConnectionString();
+ //act
+ var result = await sut.GetCurrent([id], CancellationToken.None);
+
+
+ //assert
+ result.ShouldNotBeNull();
+ result.ShouldNotBeEmpty();
+
+ var setpoint = result.First();
+
+ setpoint.Value.ShouldNotBeNull();
+ setpoint
+ .Value.ShouldBeOfType()
+ .ValueKind.ShouldBe(JsonValueKind.Number);
+ }
+
+ private JsonElement GetJsonFromObject(object value)
+ {
+ var jsonString = JsonSerializer.Serialize(value);
+ var doc = JsonDocument.Parse(jsonString);
+ return doc.RootElement;
+ }
+
+}
diff --git a/DD.Persistence.Repository/Extensions/EFExtensionsSortBy.cs b/DD.Persistence.Repository/Extensions/EFExtensionsSortBy.cs
index bc03e3b..bed529e 100644
--- a/DD.Persistence.Repository/Extensions/EFExtensionsSortBy.cs
+++ b/DD.Persistence.Repository/Extensions/EFExtensionsSortBy.cs
@@ -6,7 +6,7 @@ namespace DD.Persistence.Repository.Extensions;
public static class EFExtensionsSortBy
{
- struct TypeAccessor
+ public struct TypeAccessor
{
public LambdaExpression KeySelector { get; set; }
public MethodInfo OrderBy { get; set; }
@@ -26,6 +26,42 @@ public static class EFExtensionsSortBy
private static readonly MethodInfo methodThenByDescending = GetExtOrderMethod("ThenByDescending");
+ public static Func> sortOrder =
+ (Type rootType, Type? type, TypeAccessor? accessor) =>
+ {
+ if (type is null && accessor.HasValue)
+ {
+ var accessorValue = accessor.Value;
+ return Tuple.Create(
+ accessorValue.OrderBy,
+ accessorValue.OrderByDescending
+ );
+ }
+
+ return Tuple.Create(
+ methodOrderBy.MakeGenericMethod(rootType, type!),
+ methodOrderByDescending.MakeGenericMethod(rootType, type!)
+ );
+ };
+
+ public static Func> thenSortOrder =
+ (Type rootType, Type? type, TypeAccessor? accessor) =>
+ {
+ if (type is null && accessor.HasValue)
+ {
+ var accessorValue = accessor.Value;
+ return Tuple.Create(
+ accessorValue.ThenBy,
+ accessorValue.ThenByDescending
+ );
+ }
+
+ return Tuple.Create(
+ methodThenBy.MakeGenericMethod(rootType, type!),
+ methodThenByDescending.MakeGenericMethod(rootType, type!)
+ );
+ };
+
private static MethodInfo GetExtOrderMethod(string methodName)
=> typeof(Queryable)
.GetMethods()
@@ -71,7 +107,7 @@ public static class EFExtensionsSortBy
/// и опционально указания направления сортировки "asc" или "desc"
///
///
- /// var query = query("Timestamp desc");
+ /// var query = query("Date desc");
///
/// Запрос с примененной сортировкой
public static IOrderedQueryable SortBy(
@@ -83,10 +119,11 @@ public static class EFExtensionsSortBy
var sortEnum = propertySorts.GetEnumerator();
sortEnum.MoveNext();
- var orderedQuery = query.SortBy(sortEnum.Current);
+
+ var orderedQuery = query.SortBy(sortOrder, sortEnum.Current);
while (sortEnum.MoveNext())
- orderedQuery = orderedQuery.ThenSortBy(sortEnum.Current);
+ orderedQuery = orderedQuery.SortBy(thenSortOrder, sortEnum.Current);
return orderedQuery;
}
@@ -103,44 +140,19 @@ public static class EFExtensionsSortBy
/// и опционально указания направления сортировки "asc" или "desc"
///
///
- /// var query = query("Timestamp desc");
+ /// var query = query("Date desc");
///
/// Запрос с примененной сортировкой
public static IOrderedQueryable SortBy(
this IQueryable query,
+ Func> orderMethod,
string propertySort)
{
var parts = propertySort.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries);
var isDesc = parts.Length >= 2 && parts[1].ToLower().Trim() == "desc";
var propertyName = parts[0];
- var newQuery = query.SortBy(propertyName, isDesc);
- return newQuery;
- }
-
- ///
- /// Добавить в запрос дополнительную сортировку по возрастанию или убыванию.
- ///
- ///
- ///
- ///
- /// Свойство сортировки.
- /// Состоит из названия свойства (в любом регистре)
- /// и опционально указания направления сортировки "asc" или "desc"
- ///
- ///
- /// var query = query("Timestamp desc");
- ///
- /// Запрос с примененной сортировкой
- public static IOrderedQueryable ThenSortBy(
- this IOrderedQueryable query,
- string propertySort)
- {
- var parts = propertySort.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries);
- var isDesc = parts.Length >= 2 && parts[1].ToLower().Trim() == "desc";
- var propertyName = parts[0];
-
- var newQuery = query.ThenSortBy(propertyName, isDesc);
+ var newQuery = query.SortBy(orderMethod, propertyName, isDesc);
return newQuery;
}
@@ -154,25 +166,27 @@ public static class EFExtensionsSortBy
/// Запрос с примененной сортировкой
public static IOrderedQueryable SortBy(
this IQueryable query,
+ Func> orderMethod,
string propertyName,
bool isDesc)
{
Type rootType = typeof(TSource);
- var typePropSelector = TypePropSelectors.GetOrAdd(rootType, MakeTypeAccessors);
- var propertyNameLower = propertyName.ToLower();
MethodInfo orderByDescending;
MethodInfo orderByAscending;
+ TypeAccessor? rootTypeAccessor = null;
+ Type? type = null;
LambdaExpression? lambdaExpression = null;
- if (propertyName.Contains('.'))
+ const string Separator = ".";
+ if (propertyName.Contains(Separator))
{
- Type type = rootType;
+ type = rootType;
ParameterExpression rootExpression = Expression.Parameter(rootType, "x");
Expression expr = rootExpression;
- var propertyPath = propertyName.Split(".", StringSplitOptions.RemoveEmptyEntries);
+ var propertyPath = propertyName.Split(Separator, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < propertyPath.Length; i++)
{
@@ -184,75 +198,24 @@ public static class EFExtensionsSortBy
Type delegateType = typeof(Func<,>).MakeGenericType(rootType, type);
lambdaExpression = Expression.Lambda(delegateType, expr, rootExpression);
- orderByAscending = methodOrderBy.MakeGenericMethod(rootType, type);
- orderByDescending = methodOrderByDescending.MakeGenericMethod(rootType, type);
+ Tuple order = orderMethod
+ .Invoke(rootType, type, null);
+ orderByAscending = order.Item1;
+ orderByDescending = order.Item2;
}
else
{
- var rootTypeAccessor = typePropSelector[propertyNameLower];
- orderByAscending = rootTypeAccessor.OrderBy;
- orderByDescending = rootTypeAccessor.OrderByDescending;
- lambdaExpression = rootTypeAccessor.KeySelector;
- }
+ var typePropSelector = TypePropSelectors.GetOrAdd(rootType, MakeTypeAccessors);
+ var propertyNameLower = propertyName.ToLower();
- var genericMethod = isDesc
- ? orderByDescending
- : orderByAscending;
+ rootTypeAccessor = typePropSelector[propertyNameLower];
- var newQuery = (IOrderedQueryable)genericMethod
- .Invoke(genericMethod, [query, lambdaExpression])!;
- return newQuery;
- }
+ Tuple order = orderMethod
+ .Invoke(rootType, type, rootTypeAccessor);
+ orderByAscending = order.Item1;
+ orderByDescending = order.Item2;
- ///
- /// Добавить в запрос дополнительную сортировку по возрастанию или убыванию
- ///
- ///
- ///
- /// Название свойства (в любом регистре)
- /// Сортировать по убыванию
- /// Запрос с примененной сортировкой
- public static IOrderedQueryable ThenSortBy(
- this IOrderedQueryable query,
- string propertyName,
- bool isDesc)
- {
- Type rootType = typeof(TSource);
- var typePropSelector = TypePropSelectors.GetOrAdd(rootType, MakeTypeAccessors);
- var propertyNameLower = propertyName.ToLower();
-
- MethodInfo orderByDescending;
- MethodInfo orderByAscending;
-
- LambdaExpression? lambdaExpression = null;
-
- if (propertyName.Contains('.'))
- {
- Type type = rootType;
- ParameterExpression rootExpression = Expression.Parameter(rootType, "x");
- Expression expr = rootExpression;
-
- var propertyPath = propertyName.Split(".", StringSplitOptions.RemoveEmptyEntries);
-
- for (int i = 0; i < propertyPath.Length; i++)
- {
- PropertyInfo pi = type.GetProperty(propertyPath[i])!;
- expr = Expression.Property(expr, pi);
- type = pi.PropertyType;
- }
-
- Type delegateType = typeof(Func<,>).MakeGenericType(rootType, type);
- lambdaExpression = Expression.Lambda(delegateType, expr, rootExpression);
-
- orderByAscending = methodThenBy.MakeGenericMethod(rootType, type);
- orderByDescending = methodThenByDescending.MakeGenericMethod(rootType, type);
- }
- else
- {
- var rootTypeAccessor = typePropSelector[propertyNameLower];
- orderByAscending = rootTypeAccessor.ThenBy;
- orderByDescending = rootTypeAccessor.ThenByDescending;
- lambdaExpression = rootTypeAccessor.KeySelector;
+ lambdaExpression = rootTypeAccessor.Value.KeySelector;
}
var genericMethod = isDesc
diff --git a/DD.Persistence.Repository/Repositories/SetpointRepository.cs b/DD.Persistence.Repository/Repositories/SetpointRepository.cs
index 8fb703c..8c3ae65 100644
--- a/DD.Persistence.Repository/Repositories/SetpointRepository.cs
+++ b/DD.Persistence.Repository/Repositories/SetpointRepository.cs
@@ -1,8 +1,9 @@
-using Mapster;
+using Mapster;
using Microsoft.EntityFrameworkCore;
using DD.Persistence.Database.Model;
using DD.Persistence.Models;
using DD.Persistence.Repositories;
+using System.Text.Json;
using DD.Persistence.Models.Common;
namespace DD.Persistence.Repository.Repositories
@@ -17,16 +18,33 @@ namespace DD.Persistence.Repository.Repositories
protected virtual IQueryable GetQueryReadOnly() => db.Set();
- public async Task> GetCurrent(IEnumerable setpointKeys, CancellationToken token)
+ public async Task> GetCurrent(
+ IEnumerable setpointKeys,
+ CancellationToken token)
{
var query = GetQueryReadOnly();
+
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
+ .GroupBy(e => e.Key)
+ .Select(g => g.OrderByDescending(x => x.Timestamp).FirstOrDefault())
.ToArrayAsync(token);
- var dtos = entities.Select(e => e.Adapt());
+ var dtos = entities.Select(e => e.Adapt());
return dtos;
}
+ public async Task> GetCurrentDictionary(IEnumerable setpointKeys, CancellationToken token)
+ {
+ var query = GetQueryReadOnly();
+
+ var entities = await query
+ .Where(e => setpointKeys.Contains(e.Key))
+ .GroupBy(e => e.Key)
+ .Select(g => g.OrderByDescending(x => x.Timestamp).FirstOrDefault())
+ .ToDictionaryAsync(x=> x.Key, x => (object)x.Value, token);
+
+ return entities;
+ }
public async Task> GetHistory(IEnumerable setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
{
@@ -89,7 +107,7 @@ namespace DD.Persistence.Repository.Repositories
return dtos;
}
- public async Task Add(Guid setpointKey, object newValue, Guid idUser, CancellationToken token)
+ public async Task Add(Guid setpointKey, JsonElement newValue, Guid idUser, CancellationToken token)
{
var entity = new Setpoint()
{
@@ -102,5 +120,7 @@ namespace DD.Persistence.Repository.Repositories
await db.Set().AddAsync(entity, token);
await db.SaveChangesAsync(token);
}
+
+
}
}
diff --git a/DD.Persistence.sln b/DD.Persistence.sln
index 5fb5aee..eeb198c 100644
--- a/DD.Persistence.sln
+++ b/DD.Persistence.sln
@@ -21,6 +21,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DD.Persistence.App", "DD.Pe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DD.Persistence.Models", "DD.Persistence.Models\DD.Persistence.Models.csproj", "{698B4571-BB7A-4A42-8B0B-6C7F2F5360FB}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DD.Persistence.Repository.Test", "DD.Persistence.Repository.Test\DD.Persistence.Repository.Test.csproj", "{08B03623-A1C9-482F-B60E-09F293E04999}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{36D591C7-65C7-A0D1-1CBC-10CDE441BDC8}"
+ ProjectSection(SolutionItems) = preProject
+ Directory.Build.props = Directory.Build.props
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -63,6 +70,10 @@ Global
{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
+ {08B03623-A1C9-482F-B60E-09F293E04999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {08B03623-A1C9-482F-B60E-09F293E04999}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {08B03623-A1C9-482F-B60E-09F293E04999}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {08B03623-A1C9-482F-B60E-09F293E04999}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/DD.Persistence/API/ISetpointApi.cs b/DD.Persistence/API/ISetpointApi.cs
index f600f99..138e336 100644
--- a/DD.Persistence/API/ISetpointApi.cs
+++ b/DD.Persistence/API/ISetpointApi.cs
@@ -14,7 +14,7 @@ public interface ISetpointApi : ISyncApi
/// ключи уставок
///
///
- Task>> GetCurrent(IEnumerable setpoitKeys, CancellationToken token);
+ Task>> GetCurrent(IEnumerable setpoitKeys, CancellationToken token);
///
/// Получить значения уставок за определенный момент времени
diff --git a/DD.Persistence/Repositories/ISetpointRepository.cs b/DD.Persistence/Repositories/ISetpointRepository.cs
index ecce8e0..7ece9cb 100644
--- a/DD.Persistence/Repositories/ISetpointRepository.cs
+++ b/DD.Persistence/Repositories/ISetpointRepository.cs
@@ -1,5 +1,6 @@
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
+using System.Text.Json;
namespace DD.Persistence.Repositories;
@@ -16,6 +17,14 @@ public interface ISetpointRepository
///
Task> GetCurrent(IEnumerable setpointKeys, CancellationToken token);
+ ///
+ /// Получить значения уставок по набору ключей
+ ///
+ ///
+ ///
+ ///
+ Task> GetCurrentDictionary(IEnumerable setpointKeys, CancellationToken token);
+
///
/// Получить значения уставок за определенный момент времени
///
@@ -59,5 +68,5 @@ public interface ISetpointRepository
///
/// to do
/// id User учесть в соответствующем методе репозитория
- Task Add(Guid setpointKey, object newValue, Guid idUser, CancellationToken token);
+ Task Add(Guid setpointKey, JsonElement newValue, Guid idUser, CancellationToken token);
}
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..cffb4fe
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,15 @@
+
+
+
+
+
+ <_Parameter1>$(AssemblyName).Test
+
+
+ <_Parameter1>DD.Persistence.IntegrationTests
+
+
+ <_Parameter1>DynamicProxyGenAssembly2
+
+
+
\ No newline at end of file