diff --git a/DD.Persistence.App/appsettings.Tests.json b/DD.Persistence.App/appsettings.Tests.json index 72c43d3..e8d3cc4 100644 --- a/DD.Persistence.App/appsettings.Tests.json +++ b/DD.Persistence.App/appsettings.Tests.json @@ -1,10 +1,10 @@ { "DbConnection": { - "Host": "postgres", + "Host": "localhost", "Port": 5432, "Database": "persistence", "Username": "postgres", - "Password": "postgres" + "Password": "q" }, "NeedUseKeyCloak": false, "AuthUser": { diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs index c193fc5..9c9f262 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs @@ -77,4 +77,28 @@ public interface ITimestampedValuesClient : IDisposable /// Дискриминатор (идентификатор) набора /// Task GetDatesRange(Guid discriminatorId, CancellationToken token); + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); + + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task> GetLast(Guid idDiscriminator, int take, CancellationToken token); } \ No newline at end of file diff --git a/DD.Persistence.Client/Clients/TimestampedValuesClient.cs b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs index 19fce3f..bafead6 100644 --- a/DD.Persistence.Client/Clients/TimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs @@ -4,6 +4,7 @@ using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Models; using DD.Persistence.Models.Common; using Microsoft.Extensions.Logging; +using System.Collections.Concurrent; namespace DD.Persistence.Client.Clients; @@ -18,6 +19,9 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create(); } + /// + private readonly ConcurrentDictionary mapperCache = new(); + /// public async Task AddRange(Guid discriminatorId, IEnumerable sets, CancellationToken token) { @@ -89,6 +93,30 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient return result; } + /// + public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + { + var data = await Get(idDiscriminator, geTimestamp, columnNames, skip, take, token); + var mapper = GetMapper(idDiscriminator); + + return data.Select(mapper.DeserializeTimeStampedData); + } + + /// + public async Task> GetLast(Guid idDiscriminator, int take, CancellationToken token) + { + var data = await GetLast(idDiscriminator, take, token); + var mapper = GetMapper(idDiscriminator); + + return data.Select(mapper.DeserializeTimeStampedData); + } + + /// + private TimestampedSetMapper GetMapper(Guid idDiscriminator) + { + return (TimestampedSetMapper)mapperCache.GetOrAdd(idDiscriminator, name => new TimestampedSetMapper(idDiscriminator)); + } + /// public void Dispose() { diff --git a/DD.Persistence.Client/DD.Persistence.Client.csproj b/DD.Persistence.Client/DD.Persistence.Client.csproj index 3491596..1e0bc4a 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.4.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)).1 + 1.5.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)).1 - 1.4.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)).1 + 1.5.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)).1 DD.Persistence.Client @@ -40,8 +40,8 @@ - 1.4.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) - 1.4.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) + 1.5.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) + 1.5.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) diff --git a/DD.Persistence.Client/TimestampedSetMapper.cs b/DD.Persistence.Client/TimestampedSetMapper.cs new file mode 100644 index 0000000..cf7463a --- /dev/null +++ b/DD.Persistence.Client/TimestampedSetMapper.cs @@ -0,0 +1,135 @@ +using DD.Persistence.Models; +using System.Collections.Concurrent; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text.Json; + +namespace DD.Persistence.Client; + +internal abstract class TimestampedSetMapperBase +{ + public abstract object Map(TimestampedValuesDto data); + +} +internal class TimestampedSetMapper : TimestampedSetMapperBase +{ + private readonly Type entityType = typeof(T); + public Guid IdDiscriminator { get; } + private readonly ConcurrentDictionary PropertyCache = new(); + + public TimestampedSetMapper(Guid idDiscriminator) + { + IdDiscriminator = idDiscriminator; + } + + public override object Map(TimestampedValuesDto data) + { + return DeserializeTimeStampedData(data)!; + } + + public T DeserializeTimeStampedData(TimestampedValuesDto data) + { + + if (entityType.IsValueType) + return MapStruct(data); + else + return MapClass(data); + } + + private T MapClass(TimestampedValuesDto data) + { + var entity = (T)RuntimeHelpers.GetUninitializedObject(typeof(T)); + foreach (var (propertyName, value) in data.Values) + { + if (value is JsonElement jsonElement) + SetPropertyValueFromJson(ref entity, propertyName, jsonElement); + } + SetPropertyValue(ref entity, "Timestamp", data.Timestamp); + return entity; + } + + private T MapStruct(TimestampedValuesDto data) + { + var entity = Activator.CreateInstance(); + object boxedEntity = entity!; + foreach (var (propertyName, value) in data.Values) + { + if (value is JsonElement jsonElement) + SetPropertyValueForStructFromJson(ref boxedEntity, propertyName, jsonElement); + } + SetPropertyValueForStruct(ref boxedEntity, "Timestamp", data.Timestamp); + + return (T)boxedEntity; + } + + private void SetPropertyValueForStructFromJson(ref object entity, string propertyName, JsonElement element) + { + var property = GetPropertyInfo(propertyName); + if (property is null) + return; + + try + { + var value = element.Deserialize(property.PropertyType); + property.SetValue(entity, value); + } + catch (Exception ex) + { + } + } + private void SetPropertyValueForStruct(ref object entity, string propertyName, object value) + { + var property = GetPropertyInfo(propertyName); + if (property is null) + return; + + try + { + var convertedValue = Convert.ChangeType(value, property.PropertyType); + property.SetValue(entity, convertedValue); + } + catch (Exception ex) + { + } + } + + + private void SetPropertyValueFromJson(ref T entity, string propertyName, JsonElement jsonElement) + { + var property = GetPropertyInfo(propertyName); + + if (property is null) + return; + + try + { + var value = jsonElement.Deserialize(property.PropertyType); + property.SetValue(entity, value); + } + catch (Exception ex) + { + + } + } + + private void SetPropertyValue(ref T entity, string propertyName, object value) + { + var property = GetPropertyInfo(propertyName); + if (property is null) + return; + + try + { + var convertedValue = Convert.ChangeType(value, property.PropertyType); + property.SetValue(entity, convertedValue); + } + catch (Exception ex) + { + } + } + + private PropertyInfo? GetPropertyInfo(string propertyName) + { + return PropertyCache.GetOrAdd(propertyName, name => entityType.GetProperty(name)); + } +} diff --git a/DD.Persistence.Models/DD.Persistence.Models.csproj b/DD.Persistence.Models/DD.Persistence.Models.csproj index 6bcae66..6facfdc 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.2.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) + 1.3.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) - 1.2.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) + 1.3.$([System.DateTime]::UtcNow.ToString(yyMM.ddHH)) DD.Persistence.Models