diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs index 316aab3..9565292 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs @@ -1,4 +1,4 @@ -using DD.Persistence.Models; +using DD.Persistence.Models; using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients.Interfaces; @@ -37,18 +37,6 @@ public interface ITimestampedValuesClient : IDisposable int take, CancellationToken token); - /// - /// Получить данные с фильтрацией для нескольких систем. Значение фильтра null - отключен - /// - /// - /// - /// - /// Фильтр свойств набора - /// - /// - /// - Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token); - /// /// Получить данные, начиная с заданной отметки времени /// @@ -97,13 +85,6 @@ public interface ITimestampedValuesClient : IDisposable /// Task GetDatesRange(Guid discriminatorId, CancellationToken token); - /// - /// - /// - /// - /// - /// - /// - /// - Task> GetLast(Guid idDiscriminator, int take, CancellationToken token); + + } \ No newline at end of file diff --git a/DD.Persistence.Client/Clients/Mapping/Abstractions/IMapperStorage.cs b/DD.Persistence.Client/Clients/Mapping/Abstractions/IMapperStorage.cs new file mode 100644 index 0000000..dd08e76 --- /dev/null +++ b/DD.Persistence.Client/Clients/Mapping/Abstractions/IMapperStorage.cs @@ -0,0 +1,6 @@ +namespace DD.Persistence.Client.Clients.Mapping.Abstractions; +internal interface IMapperStorage +{ + TimestampedSetMapper GetMapper(Guid idDiscriminator); + TimestampedSetMapper? GetMapper(Guid idDiscriminator); +} diff --git a/DD.Persistence.Client/Clients/Mapping/Abstractions/ISetpointMappingClient.cs b/DD.Persistence.Client/Clients/Mapping/Abstractions/ISetpointMappingClient.cs new file mode 100644 index 0000000..02f64b0 --- /dev/null +++ b/DD.Persistence.Client/Clients/Mapping/Abstractions/ISetpointMappingClient.cs @@ -0,0 +1,6 @@ +using DD.Persistence.Client.Clients.Interfaces; + +namespace DD.Persistence.Client.Clients.Mapping.Abstractions; +public interface ISetpointMappingClient : ISetpointClient +{ +} diff --git a/DD.Persistence.Client/Clients/Mapping/Abstractions/ITimestampedMappingClient.cs b/DD.Persistence.Client/Clients/Mapping/Abstractions/ITimestampedMappingClient.cs new file mode 100644 index 0000000..6be0e58 --- /dev/null +++ b/DD.Persistence.Client/Clients/Mapping/Abstractions/ITimestampedMappingClient.cs @@ -0,0 +1,45 @@ +using DD.Persistence.Client.Clients.Interfaces; + +namespace DD.Persistence.Client.Clients.Mapping.Abstractions; + +/// +/// Маппинг - обертка для клиента по работе с данными +/// +public interface ITimestampedMappingClient : ITimestampedValuesClient +{ + /// + /// Получить данные с преобразованием к заданному типу + /// + /// + /// + /// Фильтр свойств набора + /// + /// + /// + /// + Task> GetMapped(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token); + + /// + /// Получить набор данных, преобразованных к соответствующим типам из заданного конфига + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task>> GetMultiMapped(IEnumerable discriminatorIds, DateTimeOffset? timestampBegin, string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token); + + + /// + /// Получить данные с конца с преобразованием к заданному типу + /// + /// + /// + /// + /// + /// + Task> GetLastMapped(Guid idDiscriminator, int take, CancellationToken token); +} diff --git a/DD.Persistence.Client/Clients/Mapping/Clients/SetpointMappingClient.cs b/DD.Persistence.Client/Clients/Mapping/Clients/SetpointMappingClient.cs new file mode 100644 index 0000000..1942ad4 --- /dev/null +++ b/DD.Persistence.Client/Clients/Mapping/Clients/SetpointMappingClient.cs @@ -0,0 +1,103 @@ +using DD.Persistence.Client.Clients.Interfaces; +using DD.Persistence.Client.Clients.Mapping.Abstractions; +using DD.Persistence.Models; +using DD.Persistence.Models.Common; +using DD.Persistence.Models.Configurations; +using System.Text.Json; + +namespace DD.Persistence.Client.Clients.Mapping.Clients; + +/// +public class SetpointMappingClient : ISetpointMappingClient +{ + private readonly ISetpointClient setpointClient; + private readonly MappingConfig mappingConfigs; + + /// + public SetpointMappingClient(ISetpointClient setpointClient, MappingConfig mappingConfigs) + { + this.setpointClient = setpointClient; + this.mappingConfigs = mappingConfigs; + } + + /// + public async Task> GetCurrent(IEnumerable setpointKeys, CancellationToken token) + => (await setpointClient.GetCurrent(setpointKeys, token)) + .Select(x => new SetpointValueDto + { + Key = x.Key, + Value = DeserializeValue(x.Key, (JsonElement)x.Value) + }); + + /// + public async Task> GetCurrentDictionary(IEnumerable setpointConfigs, CancellationToken token) + { + var result = (await setpointClient.GetCurrent(setpointConfigs, token)) + .ToDictionary(x => x.Key, x => DeserializeValue(x.Key, (JsonElement)x.Value)); + + return result; + } + + /// + public async Task> GetHistory(IEnumerable setpointKeys, DateTimeOffset historyMoment, CancellationToken token) + { + var result = await setpointClient.GetHistory(setpointKeys, historyMoment, token); + + foreach (var dto in result) + dto.Value = DeserializeValue(dto.Key, (JsonElement)dto.Value); + + return result; + } + + /// + public async Task>> GetLog(IEnumerable setpointKeys, CancellationToken token) + { + var result = await setpointClient.GetLog(setpointKeys, token); + + foreach (var item in result) + DeserializeList(result[item.Key]); + + return result; + } + + /// + public async Task> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token) + { + var result = await setpointClient.GetPart(dateBegin, take, token); + + DeserializeList(result); + + return result; + } + + /// + public async Task Add(Guid setpointKey, object newValue, CancellationToken token) + => await setpointClient.Add(setpointKey, newValue, token); + + /// + public async Task GetDatesRangeAsync(CancellationToken token) + => await setpointClient.GetDatesRangeAsync(token); + + /// + public void Dispose() + { + setpointClient.Dispose(); + + } + + private object DeserializeValue(Guid key, JsonElement value) + { + if (mappingConfigs.TryGetValue(key, out var type)) + return value.Deserialize(type)!; + + return value; + } + private void DeserializeList(IEnumerable? result) + { + if (result is null) + return; + + foreach (var log in result) + log.Value = DeserializeValue(log.Key, (JsonElement)log.Value); + } +} diff --git a/DD.Persistence.Client/Clients/Mapping/Clients/TimestampedMappingClient.cs b/DD.Persistence.Client/Clients/Mapping/Clients/TimestampedMappingClient.cs new file mode 100644 index 0000000..09aa283 --- /dev/null +++ b/DD.Persistence.Client/Clients/Mapping/Clients/TimestampedMappingClient.cs @@ -0,0 +1,107 @@ +using DD.Persistence.Client.Clients.Interfaces; +using DD.Persistence.Client.Clients.Mapping.Abstractions; +using DD.Persistence.Models; +using DD.Persistence.Models.Common; +using Microsoft.Extensions.Logging; + +namespace DD.Persistence.Client.Clients.Mapping.Clients; + +/// +internal class TimestampedMappingClient : ITimestampedMappingClient +{ + private readonly ITimestampedValuesClient client; + private readonly IMapperStorage mapperStorage; + + public TimestampedMappingClient(ITimestampedValuesClient client, IMapperStorage mapperStorage) + { + this.client = client; + this.mapperStorage = mapperStorage; + } + + /// + public async Task> GetMapped(Guid discriminatorId, DateTimeOffset? geTimestamp, + string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token) + { + var data = await Get([discriminatorId], geTimestamp, filterTree, columnNames, skip, take, token); + var mapper = mapperStorage.GetMapper(discriminatorId); + + var mappedDtos = data.Select(mapper.DeserializeTimeStampedData).OfType(); + + return mappedDtos; + } + + /// + public async Task> GetLastMapped(Guid idDiscriminator, int take, CancellationToken token) + { + var data = await GetLast(idDiscriminator, take, token); + var mapper = mapperStorage.GetMapper(idDiscriminator); + + var mappedDtos = data.Select(mapper.DeserializeTimeStampedData).OfType(); + + return mappedDtos; + } + + /// + public async Task>> GetMultiMapped(IEnumerable discriminatorIds, DateTimeOffset? geTimestamp, + string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token) + { + var data = await client.Get(discriminatorIds, geTimestamp, filterTree, columnNames, skip, take, token); + + var result = discriminatorIds + .ToDictionary(discriminatorId => discriminatorId, discriminatorId => + { + var mapper = mapperStorage.GetMapper(discriminatorId); + + ArgumentNullException.ThrowIfNull(mapper); + + var mappedDtos = data + .Where(e => e.DiscriminatorId == discriminatorId) + .Select(mapper.DeserializeTimeStampedData); + + return mappedDtos; + }); + + return result; + } + + /// + public async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) + => await client.AddRange(discriminatorId, dtos, token); + + /// + public async Task Count(Guid discriminatorId, CancellationToken token) + => await client.Count(discriminatorId, token); + + /// + public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? timestampBegin, + string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token) + => await client.Get(discriminatorIds, timestampBegin, filterTree, columnNames, skip, take, token); + + /// + public async Task GetDatesRange(Guid discriminatorId, CancellationToken token) + => await client.GetDatesRange(discriminatorId, token); + + /// + public async Task> GetFirst(Guid discriminatorId, int take, CancellationToken token) + => await client.GetFirst(discriminatorId, take, token); + + /// + public async Task> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token) + => await client.GetGtDate(discriminatorId, timestampBegin, token); + + /// + public async Task> GetLast(Guid discriminatorId, int take, CancellationToken token) + => await client.GetLast(discriminatorId, take, token); + + /// + public async Task> GetResampledData(Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default) + => await client.GetResampledData(discriminatorId, timestampBegin, intervalSec, approxPointsCount, token); + + /// + public void Dispose() + { + client.Dispose(); + } + + +} diff --git a/DD.Persistence.Client/Clients/Mapping/MapperStorage.cs b/DD.Persistence.Client/Clients/Mapping/MapperStorage.cs new file mode 100644 index 0000000..74d33d7 --- /dev/null +++ b/DD.Persistence.Client/Clients/Mapping/MapperStorage.cs @@ -0,0 +1,33 @@ +using DD.Persistence.Client.Clients.Mapping.Abstractions; +using DD.Persistence.Models.Configurations; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; + +namespace DD.Persistence.Client.Clients.Mapping; +internal class MapperStorage : IMapperStorage +{ + private readonly ConcurrentDictionary mapperCache = new(); + private readonly MappingConfig mappingConfigs; + private readonly ILogger logger; + + public MapperStorage(MappingConfig mappingConfigs, ILogger logger) + { + this.mappingConfigs = mappingConfigs; + this.logger = logger; + } + + + public TimestampedSetMapper? GetMapper(Guid idDiscriminator) + { + if (mappingConfigs.TryGetValue(idDiscriminator, out var type)) + return mapperCache.GetOrAdd(idDiscriminator, name => new TimestampedSetMapper(idDiscriminator, type, logger)); + + return null; + } + + public TimestampedSetMapper GetMapper(Guid idDiscriminator) + { + return mapperCache.GetOrAdd(idDiscriminator, name => new TimestampedSetMapper(idDiscriminator, typeof(T), logger)); + } +} diff --git a/DD.Persistence.Client/TimestampedSetMapper.cs b/DD.Persistence.Client/Clients/Mapping/TimestampedSetMapper.cs similarity index 59% rename from DD.Persistence.Client/TimestampedSetMapper.cs rename to DD.Persistence.Client/Clients/Mapping/TimestampedSetMapper.cs index cf7463a..38c1bea 100644 --- a/DD.Persistence.Client/TimestampedSetMapper.cs +++ b/DD.Persistence.Client/Clients/Mapping/TimestampedSetMapper.cs @@ -1,65 +1,62 @@ using DD.Persistence.Models; +using Microsoft.Extensions.Logging; using System.Collections.Concurrent; using System.Reflection; using System.Runtime.CompilerServices; using System.Text.Json; -namespace DD.Persistence.Client; +namespace DD.Persistence.Client.Clients.Mapping; -internal abstract class TimestampedSetMapperBase +internal class TimestampedSetMapper { - public abstract object Map(TimestampedValuesDto data); + private readonly Type entityType; + private readonly ILogger logger; -} -internal class TimestampedSetMapper : TimestampedSetMapperBase -{ - private readonly Type entityType = typeof(T); public Guid IdDiscriminator { get; } private readonly ConcurrentDictionary PropertyCache = new(); - public TimestampedSetMapper(Guid idDiscriminator) + public TimestampedSetMapper(Guid idDiscriminator, Type entityType, ILogger logger) { IdDiscriminator = idDiscriminator; + this.entityType = entityType; + this.logger = logger; } - public override object Map(TimestampedValuesDto data) + public object DeserializeTimeStampedData(TimestampedValuesDto data) { - return DeserializeTimeStampedData(data)!; - } - - public T DeserializeTimeStampedData(TimestampedValuesDto data) - { - if (entityType.IsValueType) return MapStruct(data); - else - return MapClass(data); + + return MapClass(data); } - private T MapClass(TimestampedValuesDto data) + private object MapClass(TimestampedValuesDto data) { - var entity = (T)RuntimeHelpers.GetUninitializedObject(typeof(T)); + var entity = RuntimeHelpers.GetUninitializedObject(entityType); foreach (var (propertyName, value) in data.Values) { if (value is JsonElement jsonElement) SetPropertyValueFromJson(ref entity, propertyName, jsonElement); } - SetPropertyValue(ref entity, "Timestamp", data.Timestamp); + SetPropertyValue(ref entity, nameof(TimestampedValuesDto.Timestamp), data.Timestamp); + SetPropertyValue(ref entity, nameof(TimestampedValuesDto.DiscriminatorId), data.DiscriminatorId); + return entity; } - private T MapStruct(TimestampedValuesDto data) + private object MapStruct(TimestampedValuesDto data) { - var entity = Activator.CreateInstance(); + var entity = Activator.CreateInstance(entityType); 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); + SetPropertyValueForStruct(ref boxedEntity, nameof(TimestampedValuesDto.Timestamp), data.Timestamp); + SetPropertyValueForStruct(ref boxedEntity, nameof(TimestampedValuesDto.DiscriminatorId), data.DiscriminatorId); - return (T)boxedEntity; + return boxedEntity; } private void SetPropertyValueForStructFromJson(ref object entity, string propertyName, JsonElement element) @@ -73,8 +70,9 @@ internal class TimestampedSetMapper : TimestampedSetMapperBase var value = element.Deserialize(property.PropertyType); property.SetValue(entity, value); } - catch (Exception ex) + catch (Exception ex) { + logger.LogError(ex.Message); } } private void SetPropertyValueForStruct(ref object entity, string propertyName, object value) @@ -88,13 +86,14 @@ internal class TimestampedSetMapper : TimestampedSetMapperBase var convertedValue = Convert.ChangeType(value, property.PropertyType); property.SetValue(entity, convertedValue); } - catch (Exception ex) + catch (Exception ex) { + logger.LogError(ex.Message); } } - private void SetPropertyValueFromJson(ref T entity, string propertyName, JsonElement jsonElement) + private void SetPropertyValueFromJson(ref object entity, string propertyName, JsonElement jsonElement) { var property = GetPropertyInfo(propertyName); @@ -106,13 +105,13 @@ internal class TimestampedSetMapper : TimestampedSetMapperBase var value = jsonElement.Deserialize(property.PropertyType); property.SetValue(entity, value); } - catch (Exception ex) + catch (Exception ex) { - + logger.LogError(ex.Message); } } - private void SetPropertyValue(ref T entity, string propertyName, object value) + private void SetPropertyValue(ref object entity, string propertyName, object value) { var property = GetPropertyInfo(propertyName); if (property is null) @@ -123,8 +122,9 @@ internal class TimestampedSetMapper : TimestampedSetMapperBase var convertedValue = Convert.ChangeType(value, property.PropertyType); property.SetValue(entity, convertedValue); } - catch (Exception ex) + catch (Exception ex) { + logger.LogError(ex.Message); } } diff --git a/DD.Persistence.Client/Clients/SetpointClient.cs b/DD.Persistence.Client/Clients/SetpointClient.cs index dfb5026..a2c401e 100644 --- a/DD.Persistence.Client/Clients/SetpointClient.cs +++ b/DD.Persistence.Client/Clients/SetpointClient.cs @@ -3,9 +3,6 @@ using DD.Persistence.Client.Clients.Base; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Models; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Globalization; using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients; @@ -13,15 +10,12 @@ namespace DD.Persistence.Client.Clients; public class SetpointClient : BaseClient, ISetpointClient { private readonly IRefitSetpointClient refitSetpointClient; - private readonly ISetpointConfigStorage setpointConfigStorage; 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) @@ -31,7 +25,7 @@ public class SetpointClient : BaseClient, ISetpointClient return result!.Select(x => new SetpointValueDto { Key = x.Key, - Value = DeserializeValue(x.Key, x.Value) + Value = x.Value }); } @@ -43,16 +37,13 @@ public class SetpointClient : BaseClient, ISetpointClient async () => await refitSetpointClient.GetCurrent(setpointConfigs, token), token); - return result!.ToDictionary(x => x.Key,x => DeserializeValue(x.Key,x.Value)); + return result!.ToDictionary(x => x.Key,x => (object)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!; @@ -63,9 +54,6 @@ 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!; } @@ -82,8 +70,6 @@ public class SetpointClient : BaseClient, ISetpointClient var result = await ExecuteGetResponse( async () => await refitSetpointClient.GetPart(dateBegin, take, token), token); - DeserializeList(result); - return result!; } @@ -101,21 +87,4 @@ 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/Clients/TimestampedValuesClient.cs b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs index e4ad2a0..dc0699f 100644 --- a/DD.Persistence.Client/Clients/TimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs @@ -1,4 +1,4 @@ -using DD.Persistence.Client.Clients.Base; +using DD.Persistence.Client.Clients.Base; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Models; @@ -19,8 +19,7 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create(); } - /// - private readonly ConcurrentDictionary mapperCache = new(); + /// public async Task AddRange(Guid discriminatorId, IEnumerable sets, CancellationToken token) @@ -39,15 +38,6 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient return result!; } - /// - public async Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token) - { - var data = await Get([discriminatorId], geTimestamp, filterTree, columnNames, skip, take, token); - var mapper = GetMapper(discriminatorId); - - return data.Select(mapper.DeserializeTimeStampedData); - } - /// public async Task> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token) { @@ -102,20 +92,7 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient return result; } - /// - 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/DependencyInjection.cs b/DD.Persistence.Client/DependencyInjection.cs index 89f65cf..f759bd8 100644 --- a/DD.Persistence.Client/DependencyInjection.cs +++ b/DD.Persistence.Client/DependencyInjection.cs @@ -1,6 +1,10 @@ using DD.Persistence.Client.Clients; using DD.Persistence.Client.Clients.Interfaces; +using DD.Persistence.Client.Clients.Mapping; +using DD.Persistence.Client.Clients.Mapping.Abstractions; +using DD.Persistence.Client.Clients.Mapping.Clients; using DD.Persistence.Models; +using DD.Persistence.Models.Configurations; using Microsoft.Extensions.DependencyInjection; namespace DD.Persistence.Client; @@ -15,7 +19,7 @@ public static class DependencyInjection /// /// /// - public static IServiceCollection AddPersistenceClients(this IServiceCollection services, Dictionary? setpointTypeConfigs = null) + public static IServiceCollection AddPersistenceClients(this IServiceCollection services) { services.AddTransient(typeof(IRefitClientFactory<>), typeof(RefitClientFactory<>)); services.AddTransient(); @@ -25,10 +29,17 @@ public static class DependencyInjection services.AddTransient(); services.AddTransient(); - services.AddSingleton(provider => - { - return new SetpointConfigStorage(setpointTypeConfigs); - }); + return services; + } + + + public static IServiceCollection AddPersistenceMapping(this IServiceCollection services, MappingConfig mappingConfigs) + { + services.AddSingleton(mappingConfigs); + services.AddSingleton(); + + services.AddTransient(); + services.AddTransient(); return services; } } diff --git a/DD.Persistence.Client/ISetpointConfigStorage.cs b/DD.Persistence.Client/ISetpointConfigStorage.cs deleted file mode 100644 index 2c73783..0000000 --- a/DD.Persistence.Client/ISetpointConfigStorage.cs +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index 5cfbabf..0000000 --- a/DD.Persistence.Client/SetpointConfigStorage.cs +++ /dev/null @@ -1,20 +0,0 @@ -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.IntegrationTests/Controllers/SetpointControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs index 64506dc..a2080b0 100644 --- a/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs @@ -1,10 +1,10 @@ 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.Client.Clients.Mapping.Clients; using DD.Persistence.Database.Entity; +using DD.Persistence.Models.Configurations; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System.Text.Json; using Xunit; @@ -13,17 +13,14 @@ 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 .GetRequiredService>(); - var logger = scope.ServiceProvider.GetRequiredService>(); setpointClient = scope.ServiceProvider .GetRequiredService(); - configStorage = (SetpointConfigStorage)scope.ServiceProvider.GetRequiredService(); } @@ -32,12 +29,16 @@ namespace DD.Persistence.IntegrationTests.Controllers { var id = Guid.Parse("e0fcad22-1761-476e-a729-a3c59d51ba41"); - configStorage.AddOrReplace(id, typeof(float)); + var config = new MappingConfig(); + config[id] = typeof(float); + + var setpointMapper = new SetpointMappingClient(setpointClient, config); + await setpointClient.Add(id, 48.3f, CancellationToken.None); //act - var response = await setpointClient.GetCurrent([id], CancellationToken.None); + var response = await setpointMapper.GetCurrent([id], CancellationToken.None); //assert Assert.NotNull(response); diff --git a/DD.Persistence.Models/Configurations/MappingConfig.cs b/DD.Persistence.Models/Configurations/MappingConfig.cs new file mode 100644 index 0000000..bc9f85e --- /dev/null +++ b/DD.Persistence.Models/Configurations/MappingConfig.cs @@ -0,0 +1,4 @@ +namespace DD.Persistence.Models.Configurations; +public class MappingConfig : Dictionary +{ +} diff --git a/DD.Persistence.Models/TimestampedValuesDto.cs b/DD.Persistence.Models/TimestampedValuesDto.cs index 13b592e..dbb04f9 100644 --- a/DD.Persistence.Models/TimestampedValuesDto.cs +++ b/DD.Persistence.Models/TimestampedValuesDto.cs @@ -7,6 +7,11 @@ namespace DD.Persistence.Models; /// public class TimestampedValuesDto : ITimestampAbstractDto { + /// + /// Дискриминатор + /// + public Guid DiscriminatorId { get; set; } + /// /// Временная отметка /// diff --git a/DD.Persistence.Test/DD.Persistence.Test.csproj b/DD.Persistence.Test/DD.Persistence.Test.csproj index 07e0780..7006f98 100644 --- a/DD.Persistence.Test/DD.Persistence.Test.csproj +++ b/DD.Persistence.Test/DD.Persistence.Test.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -17,6 +17,7 @@ + diff --git a/DD.Persistence.Test/MappingClientsTest.cs b/DD.Persistence.Test/MappingClientsTest.cs new file mode 100644 index 0000000..b5d2e70 --- /dev/null +++ b/DD.Persistence.Test/MappingClientsTest.cs @@ -0,0 +1,99 @@ +using DD.Persistence.Client.Clients.Interfaces; +using DD.Persistence.Client.Clients.Mapping; +using DD.Persistence.Client.Clients.Mapping.Clients; +using DD.Persistence.Models; +using DD.Persistence.Models.Configurations; +using Microsoft.Extensions.Logging; +using NSubstitute; +using System.Text.Json; + +namespace DD.Persistence.Test; + +public record FirstTestDto(Guid DiscriminatorId, DateTimeOffset Timestamp, int Id, string? Value); + +public record SecondTestDto(Guid DiscriminatorId, DateTimeOffset Timestamp, int Id, double Capacity); + +public class MappingClientsTest +{ + private readonly ITimestampedValuesClient timestampedValuesClient = Substitute.For(); + private readonly ILogger logger = Substitute.For>(); + private readonly TimestampedMappingClient timestampedMappingClient; + + private readonly MappingConfig mappingConfigs; + + + public MappingClientsTest() + { + mappingConfigs = GetConfig(); + var storage = new MapperStorage(mappingConfigs, logger); + + timestampedMappingClient = new TimestampedMappingClient(timestampedValuesClient, storage); + } + + + + [Fact] + public async Task GetMultiMapped() + { + // Arrange + var discriminatorIds = mappingConfigs.Keys; + var firstDiscriminatorId = discriminatorIds.First(); + var secondDiscriminatorId = discriminatorIds.Last(); + var getResult = new[] + { + new TimestampedValuesDto() + { + DiscriminatorId = firstDiscriminatorId, + Timestamp = DateTime.UtcNow, + Values = new Dictionary + { + { nameof(FirstTestDto.Id), JsonDocument.Parse(JsonSerializer.Serialize(1)).RootElement }, + { nameof(FirstTestDto.Value), JsonDocument.Parse(JsonSerializer.Serialize("string1")).RootElement} + } + }, + new TimestampedValuesDto() + { + DiscriminatorId = secondDiscriminatorId, + Timestamp = DateTime.UtcNow, + Values = new Dictionary + { + { nameof(SecondTestDto.Id), JsonDocument.Parse(JsonSerializer.Serialize(1)).RootElement }, + { nameof(SecondTestDto.Capacity), JsonDocument.Parse(JsonSerializer.Serialize(0.1)).RootElement} + } + } + }; + timestampedValuesClient + .Get(discriminatorIds, null, null, null, 0, 1, CancellationToken.None) + .ReturnsForAnyArgs(getResult); + + // Act + var result = await timestampedMappingClient.GetMultiMapped(discriminatorIds, null, null, null, 0, 1, CancellationToken.None); + + // Assert + Assert.NotNull(result); + Assert.NotEmpty(result); + Assert.Equal(getResult.Count(), result.Count()); + + var firstActualDto = (FirstTestDto) result[firstDiscriminatorId].First(); + Assert.NotNull(firstActualDto); + + var actualId = firstActualDto.Id.ToString(); + var expectedId = getResult[0].Values[nameof(FirstTestDto.Id)].ToString(); + Assert.Equal(expectedId, actualId); + + var secondActualDto = (SecondTestDto) result[secondDiscriminatorId].First(); + Assert.NotNull(secondActualDto); + + actualId = secondActualDto.Id.ToString(); + expectedId = getResult[1].Values[nameof(SecondTestDto.Id)].ToString(); + Assert.Equal(expectedId, actualId); + } + private MappingConfig GetConfig() + { + var config = new MappingConfig(); + config[Guid.NewGuid()] = typeof(FirstTestDto); + config[Guid.NewGuid()] = typeof(SecondTestDto); + + return config; + } +} diff --git a/DD.Persistence/Services/TimestampedValuesService.cs b/DD.Persistence/Services/TimestampedValuesService.cs index f18bcd9..d94b9f4 100644 --- a/DD.Persistence/Services/TimestampedValuesService.cs +++ b/DD.Persistence/Services/TimestampedValuesService.cs @@ -122,6 +122,7 @@ public class TimestampedValuesService : ITimestampedValuesService { var dto = new TimestampedValuesDto() { + DiscriminatorId = keyValuePair.Key, Timestamp = Timestamp.ToUniversalTime(), Values = dataScheme .ToDictionary(k => k.PropertyName, v => Values[v.Index]) diff --git a/Directory.Build.props b/Directory.Build.props index cffb4fe..74c5ac9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,6 +8,9 @@ <_Parameter1>DD.Persistence.IntegrationTests + + <_Parameter1>DD.Persistence.Test + <_Parameter1>DynamicProxyGenAssembly2