#1004 Отделить логику маппинга от клиентов + #1000 Добавить множественный маппинг сущностей в TimeStampedClient #31

Merged
on.nemtina merged 11 commits from feature-mapping into master 2025-03-03 09:44:27 +05:00
7 changed files with 232 additions and 143 deletions
Showing only changes of commit efd7a7e639 - Show all commits

View File

@ -3,12 +3,12 @@
namespace DD.Persistence.Client.Clients.Mapping.Abstractions; namespace DD.Persistence.Client.Clients.Mapping.Abstractions;
/// <summary> /// <summary>
/// /// Маппинг - обертка для клиента по работе с данными
/// </summary> /// </summary>
public interface ITimestampedMappingClient : ITimestampedValuesClient public interface ITimestampedMappingClient : ITimestampedValuesClient
{ {
/// <summary> /// <summary>
/// Получить данные с фильтрацией для нескольких систем. Значение фильтра null - отключен /// Получить данные с преобразованием к заданному типу
/// </summary> /// </summary>
/// <param name="discriminatorId"></param> /// <param name="discriminatorId"></param>
/// <param name="geTimestamp"></param> /// <param name="geTimestamp"></param>
@ -17,17 +17,29 @@ public interface ITimestampedMappingClient : ITimestampedValuesClient
/// <param name="skip"></param> /// <param name="skip"></param>
/// <param name="take"></param> /// <param name="take"></param>
/// <param name="token"></param> /// <param name="token"></param>
Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token); Task<IEnumerable<T>> GetMapped<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
Task<IDictionary<Guid, object>> Gett(IEnumerable<Guid> discriminatorIds, string? filterTree, DateTimeOffset? timestampBegin, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
/// <summary>
/// Получить набор данных, преобразованных к соответствующим типам из заданного конфига
/// </summary>
/// <param name="discriminatorIds"></param>
/// <param name="timestampBegin"></param>
/// <param name="filterTree"></param>
/// <param name="columnNames"></param>
/// <param name="skip"></param>
/// <param name="take"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IDictionary<Guid, IEnumerable<object>>> GetMultiMapped(IEnumerable<Guid> discriminatorIds, DateTimeOffset? timestampBegin, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
/// <summary> /// <summary>
/// /// Получить данные с конца с преобразованием к заданному типу
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="idDiscriminator"></param> /// <param name="idDiscriminator"></param>
/// <param name="take"></param> /// <param name="take"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<IEnumerable<T>> GetLast<T>(Guid idDiscriminator, int take, CancellationToken token); Task<IEnumerable<T>> GetLastMapped<T>(Guid idDiscriminator, int take, CancellationToken token);
} }

View File

@ -5,16 +5,11 @@ using DD.Persistence.Models.Common;
using System.Text.Json; using System.Text.Json;
namespace DD.Persistence.Client.Clients.Mapping.Clients; namespace DD.Persistence.Client.Clients.Mapping.Clients;
internal class SetpointMappingClient(ISetpointClient setpointClient, Dictionary<Guid, Type> mappingConfigs) : ISetpointMappingClient
/// <inheritdoc/>
public class SetpointMappingClient(ISetpointClient setpointClient, Dictionary<Guid, Type> mappingConfigs) : ISetpointMappingClient
{ {
public async Task Add(Guid setpointKey, object newValue, CancellationToken token) /// <inheritdoc/>
=> await setpointClient.Add(setpointKey, newValue, token);
public void Dispose()
{
setpointClient.Dispose();
}
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token) public async Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token)
=> (await setpointClient.GetCurrent(setpointKeys, token)) => (await setpointClient.GetCurrent(setpointKeys, token))
.Select(x => new SetpointValueDto .Select(x => new SetpointValueDto
@ -23,15 +18,16 @@ internal class SetpointMappingClient(ISetpointClient setpointClient, Dictionary<
Value = DeserializeValue(x.Key, (JsonElement)x.Value) Value = DeserializeValue(x.Key, (JsonElement)x.Value)
}); });
/// <inheritdoc/>
public async Task<Dictionary<Guid, object>> GetCurrentDictionary(IEnumerable<Guid> setpointConfigs, CancellationToken token) public async Task<Dictionary<Guid, object>> GetCurrentDictionary(IEnumerable<Guid> setpointConfigs, CancellationToken token)
{ {
return (await setpointClient.GetCurrent(setpointConfigs, token)) var result = (await setpointClient.GetCurrent(setpointConfigs, token))
.ToDictionary(x => x.Key, x => DeserializeValue(x.Key, (JsonElement)x.Value)); .ToDictionary(x => x.Key, x => DeserializeValue(x.Key, (JsonElement)x.Value));
return result;
} }
public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token) /// <inheritdoc/>
=> await setpointClient.GetDatesRangeAsync(token);
public async Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token) public async Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
{ {
var result = await setpointClient.GetHistory(setpointKeys, historyMoment, token); var result = await setpointClient.GetHistory(setpointKeys, historyMoment, token);
@ -42,6 +38,7 @@ internal class SetpointMappingClient(ISetpointClient setpointClient, Dictionary<
return result; return result;
} }
/// <inheritdoc/>
public async Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token) public async Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token)
{ {
var result = await setpointClient.GetLog(setpointKeys, token); var result = await setpointClient.GetLog(setpointKeys, token);
@ -52,6 +49,7 @@ internal class SetpointMappingClient(ISetpointClient setpointClient, Dictionary<
return result; return result;
} }
/// <inheritdoc/>
public async Task<IEnumerable<SetpointLogDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token) public async Task<IEnumerable<SetpointLogDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token)
{ {
var res = await setpointClient.GetPart(dateBegin, take, token); var res = await setpointClient.GetPart(dateBegin, take, token);
Review

пусть будет result, также как сейчас в остальных методах

пусть будет result, также как сейчас в остальных методах
@ -61,7 +59,19 @@ internal class SetpointMappingClient(ISetpointClient setpointClient, Dictionary<
return res; return res;
} }
/// <inheritdoc/>
public async Task Add(Guid setpointKey, object newValue, CancellationToken token)
=> await setpointClient.Add(setpointKey, newValue, token);
/// <inheritdoc/>
public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token)
=> await setpointClient.GetDatesRangeAsync(token);
/// <inheritdoc/>
public void Dispose()
{
setpointClient.Dispose();
Review

добавить GC.SuppressFinalize(this);

добавить GC.SuppressFinalize(this);
}
private object DeserializeValue(Guid key, JsonElement value) private object DeserializeValue(Guid key, JsonElement value)
{ {
@ -72,8 +82,10 @@ internal class SetpointMappingClient(ISetpointClient setpointClient, Dictionary<
} }
private void DeserializeList(IEnumerable<SetpointLogDto>? result) private void DeserializeList(IEnumerable<SetpointLogDto>? result)
{ {
if (result is null)
return;
foreach (var log in result) foreach (var log in result)
log.Value = DeserializeValue(log.Key, (JsonElement)log.Value); log.Value = DeserializeValue(log.Key, (JsonElement)log.Value);
} }
} }

View File

@ -3,93 +3,123 @@ using DD.Persistence.Client.Clients.Mapping.Abstractions;
using DD.Persistence.Models; using DD.Persistence.Models;
using DD.Persistence.Models.Common; using DD.Persistence.Models.Common;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Reflection;
namespace DD.Persistence.Client.Clients.Mapping.Clients; namespace DD.Persistence.Client.Clients.Mapping.Clients;
/// <inheritdoc/>
public class TimestampedMappingClient(ITimestampedValuesClient client, Dictionary<Guid, Type>? mappingConfigs) : ITimestampedMappingClient public class TimestampedMappingClient(ITimestampedValuesClient client, Dictionary<Guid, Type>? mappingConfigs) : ITimestampedMappingClient

Нужно сделать отдельный сервис, который отвечает за кеширование маппингов, и зарегистрировать его в DI.

Нужно сделать отдельный сервис, который отвечает за кеширование маппингов, и зарегистрировать его в DI.
{ {
/// <inheritdoc/> private readonly ConcurrentDictionary<Guid, TimestampedSetMapper> mapperCache = new();
private readonly ConcurrentDictionary<Guid, TimestampedSetMapperBase> mapperCache = new();
/// <inheritdoc/>
public async Task<IEnumerable<T>> GetMapped<T>(Guid discriminatorId, DateTimeOffset? geTimestamp,
string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
{
var data = await Get([discriminatorId], geTimestamp, filterTree, columnNames, skip, take, token);
var mapper = GetMapper<T>(discriminatorId);
var mappedDtos = data.Select(mapper.DeserializeTimeStampedData).OfType<T>();
return mappedDtos;
}
/// <inheritdoc/>
public async Task<IEnumerable<T>> GetLastMapped<T>(Guid idDiscriminator, int take, CancellationToken token)
{
var data = await GetLast(idDiscriminator, take, token);
var mapper = GetMapper<T>(idDiscriminator);
var mappedDtos = data.Select(mapper.DeserializeTimeStampedData).OfType<T>();
return mappedDtos;
}
/// <inheritdoc/>
public async Task<IDictionary<Guid, IEnumerable<object>>> GetMultiMapped(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp,
string? filterTree, IEnumerable<string>? 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 =>
{
if (mappingConfigs is null)
throw new ArgumentNullException(nameof(mappingConfigs));
if (!mappingConfigs.TryGetValue(discriminatorId, out var type))
throw new InvalidCastException();
var mapper = GetMapper(discriminatorId, type);
var mappedDtos = data
.Where(e => e.DiscriminatorId == discriminatorId)
.Select(mapper.DeserializeTimeStampedData);
return mappedDtos;
});
//var genericMapperType = typeof(TimestampedSetMapper<>).MakeGenericType(type);
Review

Желательно комментарии стереть

Желательно комментарии стереть
//var ttype = typeof(TimestampedSetMapper<object>);
//object b = 0;
//var mapper = MapperMethodInfo
// .MakeGenericMethod([type])
// .Invoke(this, [discriminatorId]);
//var deserializeMethod = genericMapperType
// .GetMethod(nameof(TimestampedSetMapper<object>.DeserializeTimeStampedData))!;
// ToDo: приводим к Dictionary
return result;
}
/// <inheritdoc/>
public async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token) public async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
=> await client.AddRange(discriminatorId, dtos, token); => await client.AddRange(discriminatorId, dtos, token);
/// <inheritdoc/>
public async Task<int> Count(Guid discriminatorId, CancellationToken token) public async Task<int> Count(Guid discriminatorId, CancellationToken token)
=> await client.Count(discriminatorId, token); => await client.Count(discriminatorId, token);
/// <inheritdoc/>
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? timestampBegin,
string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
=> await client.Get(discriminatorIds, timestampBegin, filterTree, columnNames, skip, take, token);
/// <inheritdoc/>
public async Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
=> await client.GetDatesRange(discriminatorId, token);
/// <inheritdoc/>
public async Task<IEnumerable<TimestampedValuesDto>> GetFirst(Guid discriminatorId, int take, CancellationToken token)
=> await client.GetFirst(discriminatorId, take, token);
/// <inheritdoc/>
public async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
=> await client.GetGtDate(discriminatorId, timestampBegin, token);
/// <inheritdoc/>
public async Task<IEnumerable<TimestampedValuesDto>> GetLast(Guid discriminatorId, int take, CancellationToken token)
=> await client.GetLast(discriminatorId, take, token);
/// <inheritdoc/>
public async Task<IEnumerable<TimestampedValuesDto>> GetResampledData(Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default)
=> await client.GetResampledData(discriminatorId, timestampBegin, intervalSec, approxPointsCount, token);
/// <inheritdoc/>
public void Dispose() public void Dispose()
{ {
client.Dispose(); client.Dispose();
} }
/// <inheritdoc/> private TimestampedSetMapper GetMapper(Guid idDiscriminator, Type type)
public async Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
{ {
var data = await Get([discriminatorId], geTimestamp, filterTree, columnNames, skip, take, token); return mapperCache.GetOrAdd(idDiscriminator, name => new TimestampedSetMapper(idDiscriminator, type));
var mapper = GetMapper<T>(discriminatorId);
return data.Select(mapper.DeserializeTimeStampedData);
} }
/// <inheritdoc/> private TimestampedSetMapper GetMapper<T>(Guid idDiscriminator)
public async Task<IEnumerable<T>> GetLast<T>(Guid idDiscriminator, int take, CancellationToken token)
{ {
var data = await GetLast(idDiscriminator, take, token); return mapperCache.GetOrAdd(idDiscriminator, name => new TimestampedSetMapper(idDiscriminator, typeof(T)));
var mapper = GetMapper<T>(idDiscriminator);
return data.Select(mapper.DeserializeTimeStampedData);
} }
public async Task<IDictionary<Guid, object>> Gett(IEnumerable<Guid> discriminatorIds, string? filterTree, DateTimeOffset? timestampBegin, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
{
var data = await client.Get(discriminatorIds, timestampBegin, filterTree, columnNames, skip, take, token);
// ToDo: рефакторинг
foreach (var discriminatorId in discriminatorIds)
{
if (mappingConfigs.TryGetValue(discriminatorId, out var type))
{
var genericType = typeof(TimestampedSetMapper<>).MakeGenericType(type);
var mapper =
typeof(TimestampedMappingClient)
.GetMethod(nameof(GetMapper))!
.MakeGenericMethod([type])
.Invoke(this, [discriminatorId]);
var mapperInstance = Activator.CreateInstance(genericType, mapper); // ToDo: возможно не нужно
var deserializeMethod = genericType
.GetMethod("DeserializeTimeStampedData")!;
var d = data.Select(e => deserializeMethod.Invoke(mapperInstance, [e]));
// ToDo: приводим к Dictionary
}
}
return new Dictionary<Guid, object>();
}
/// <inheritdoc/>
private TimestampedSetMapper<T> GetMapper<T>(Guid idDiscriminator)
{
return (TimestampedSetMapper<T>)mapperCache.GetOrAdd(idDiscriminator, name => new TimestampedSetMapper<T>(idDiscriminator));
}
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? timestampBegin, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
=> await client.Get(discriminatorIds, timestampBegin, filterTree, columnNames, skip, take, token);
public async Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
=> await client.GetDatesRange(discriminatorId, token);
public async Task<IEnumerable<TimestampedValuesDto>> GetFirst(Guid discriminatorId, int take, CancellationToken token)
=> await client.GetFirst(discriminatorId, take, token);
public async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
=> await client.GetGtDate(discriminatorId, timestampBegin, token);
public async Task<IEnumerable<TimestampedValuesDto>> GetLast(Guid discriminatorId, int take, CancellationToken token)
=> await client.GetLast(discriminatorId, take, token);
public async Task<IEnumerable<TimestampedValuesDto>> GetResampledData(Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default)
=> await client.GetResampledData(discriminatorId, timestampBegin, intervalSec, approxPointsCount, token);
} }

View File

@ -6,60 +6,53 @@ using System.Text.Json;
namespace DD.Persistence.Client.Clients.Mapping; namespace DD.Persistence.Client.Clients.Mapping;
internal abstract class TimestampedSetMapperBase internal class TimestampedSetMapper
{ {
public abstract object Map(TimestampedValuesDto data); private readonly Type entityType;
}
internal class TimestampedSetMapper<T> : TimestampedSetMapperBase
{
private readonly Type entityType = typeof(T);
public Guid IdDiscriminator { get; } public Guid IdDiscriminator { get; }
private readonly ConcurrentDictionary<string, PropertyInfo?> PropertyCache = new(); private readonly ConcurrentDictionary<string, PropertyInfo?> PropertyCache = new();
public TimestampedSetMapper(Guid idDiscriminator) public TimestampedSetMapper(Guid idDiscriminator, Type entityType)
{ {
IdDiscriminator = idDiscriminator; IdDiscriminator = idDiscriminator;
this.entityType = entityType;
} }
public override object Map(TimestampedValuesDto data) public object DeserializeTimeStampedData(TimestampedValuesDto data)
{ {
return DeserializeTimeStampedData(data)!;
}
public T DeserializeTimeStampedData(TimestampedValuesDto data)
{
if (entityType.IsValueType) if (entityType.IsValueType)
return MapStruct(data); 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) foreach (var (propertyName, value) in data.Values)
{ {
if (value is JsonElement jsonElement) if (value is JsonElement jsonElement)
SetPropertyValueFromJson(ref entity, propertyName, 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; return entity;
} }
private T MapStruct(TimestampedValuesDto data) private object MapStruct(TimestampedValuesDto data)
{ {
var entity = Activator.CreateInstance<T>(); var entity = Activator.CreateInstance(entityType);
object boxedEntity = entity!; object boxedEntity = entity!;
foreach (var (propertyName, value) in data.Values) foreach (var (propertyName, value) in data.Values)
{ {
if (value is JsonElement jsonElement) if (value is JsonElement jsonElement)
SetPropertyValueForStructFromJson(ref boxedEntity, propertyName, 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) private void SetPropertyValueForStructFromJson(ref object entity, string propertyName, JsonElement element)
@ -73,9 +66,7 @@ internal class TimestampedSetMapper<T> : TimestampedSetMapperBase
var value = element.Deserialize(property.PropertyType); var value = element.Deserialize(property.PropertyType);
property.SetValue(entity, value); property.SetValue(entity, value);
} }
catch (Exception ex) catch (Exception) { }
{
}
} }
private void SetPropertyValueForStruct(ref object entity, string propertyName, object value) private void SetPropertyValueForStruct(ref object entity, string propertyName, object value)
{ {
@ -88,13 +79,11 @@ internal class TimestampedSetMapper<T> : TimestampedSetMapperBase
var convertedValue = Convert.ChangeType(value, property.PropertyType); var convertedValue = Convert.ChangeType(value, property.PropertyType);
property.SetValue(entity, convertedValue); property.SetValue(entity, convertedValue);
} }
catch (Exception ex) catch (Exception) { }
{
}
} }
private void SetPropertyValueFromJson(ref T entity, string propertyName, JsonElement jsonElement) private void SetPropertyValueFromJson(ref object entity, string propertyName, JsonElement jsonElement)
{ {
var property = GetPropertyInfo(propertyName); var property = GetPropertyInfo(propertyName);
@ -106,13 +95,10 @@ internal class TimestampedSetMapper<T> : TimestampedSetMapperBase
var value = jsonElement.Deserialize(property.PropertyType); var value = jsonElement.Deserialize(property.PropertyType);
property.SetValue(entity, value); property.SetValue(entity, value);
} }
catch (Exception ex) catch (Exception) { }

Тут внутри блока Exception пусто, наверное, нужна какая-то обработка?

Тут внутри блока Exception пусто, наверное, нужна какая-то обработка?
{
}
} }
private void SetPropertyValue(ref T entity, string propertyName, object value) private void SetPropertyValue(ref object entity, string propertyName, object value)
{ {
var property = GetPropertyInfo(propertyName); var property = GetPropertyInfo(propertyName);
if (property is null) if (property is null)
@ -123,9 +109,7 @@ internal class TimestampedSetMapper<T> : TimestampedSetMapperBase
var convertedValue = Convert.ChangeType(value, property.PropertyType); var convertedValue = Convert.ChangeType(value, property.PropertyType);
property.SetValue(entity, convertedValue); property.SetValue(entity, convertedValue);
} }
catch (Exception ex) catch (Exception) { }
{
}
} }
private PropertyInfo? GetPropertyInfo(string propertyName) private PropertyInfo? GetPropertyInfo(string propertyName)

View File

@ -7,6 +7,11 @@ namespace DD.Persistence.Models;
/// </summary> /// </summary>
public class TimestampedValuesDto : ITimestampAbstractDto public class TimestampedValuesDto : ITimestampAbstractDto
{ {
/// <summary>
/// Дискриминатор
/// </summary>
public Guid DiscriminatorId { get; set; }
/// <summary> /// <summary>
/// Временная отметка /// Временная отметка
/// </summary> /// </summary>

View File

@ -1,29 +1,24 @@
using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces;
using DD.Persistence.Client.Clients.Mapping.Abstractions;
using DD.Persistence.Client.Clients.Mapping.Clients; using DD.Persistence.Client.Clients.Mapping.Clients;
using DD.Persistence.Repositories; using DD.Persistence.Models;
using DD.Persistence.Services.Interfaces;
using NSubstitute; using NSubstitute;
using System; using System.Text.Json;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DD.Persistence.Test; 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 public class MappingClientsTest
{ {
private readonly ITimestampedValuesClient timestampedValuesClient = Substitute.For<ITimestampedValuesClient>(); private readonly ITimestampedValuesClient timestampedValuesClient = Substitute.For<ITimestampedValuesClient>();
private readonly TimestampedMappingClient timestampedMappingClient; private readonly TimestampedMappingClient timestampedMappingClient;
public record TestDto
{
public int Id { get; set; }
public string? Value { get; set; }
}
private readonly Dictionary<Guid, Type> mappingConfigs = new Dictionary<Guid, Type>() private readonly Dictionary<Guid, Type> mappingConfigs = new Dictionary<Guid, Type>()
{ {
{ Guid.NewGuid(), typeof(TestDto) } { Guid.NewGuid(), typeof(FirstTestDto) },
{ Guid.NewGuid(), typeof(SecondTestDto) }
}; };
public MappingClientsTest() public MappingClientsTest()
@ -32,9 +27,59 @@ public class MappingClientsTest
} }
[Fact] [Fact]
public async void Test() public async Task GetMultiMapped()
{ {
// Arrange
var discriminatorIds = mappingConfigs.Keys; var discriminatorIds = mappingConfigs.Keys;
var result = await timestampedMappingClient.Gett(discriminatorIds, null, null, null, 0, 1, CancellationToken.None); var firstDiscriminatorId = discriminatorIds.First();
var secondDiscriminatorId = discriminatorIds.Last();
var getResult = new[]
{
new TimestampedValuesDto()
{
DiscriminatorId = firstDiscriminatorId,
Timestamp = DateTime.UtcNow,
Values = new Dictionary<string, object>
{
{ 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<string, object>
{
{ 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);
} }
} }

View File

@ -122,6 +122,7 @@ public class TimestampedValuesService : ITimestampedValuesService
{ {
var dto = new TimestampedValuesDto() var dto = new TimestampedValuesDto()
{ {
DiscriminatorId = keyValuePair.Key,
Timestamp = Timestamp.ToUniversalTime(), Timestamp = Timestamp.ToUniversalTime(),
Values = dataScheme Values = dataScheme
.ToDictionary(k => k.PropertyName, v => Values[v.Index]) .ToDictionary(k => k.PropertyName, v => Values[v.Index])