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.Clients.Mapping; internal class TimestampedSetMapper { private readonly Type entityType; private readonly ILogger logger; public Guid IdDiscriminator { get; } private readonly ConcurrentDictionary PropertyCache = new(); public TimestampedSetMapper(Guid idDiscriminator, Type entityType, ILogger logger) { IdDiscriminator = idDiscriminator; this.entityType = entityType; this.logger = logger; } public object DeserializeTimeStampedData(TimestampedValuesDto data) { if (entityType.IsValueType) return MapStruct(data); return MapClass(data); } private object MapClass(TimestampedValuesDto data) { 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, nameof(TimestampedValuesDto.Timestamp), data.Timestamp); SetPropertyValue(ref entity, nameof(TimestampedValuesDto.DiscriminatorId), data.DiscriminatorId); return entity; } private object MapStruct(TimestampedValuesDto data) { 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, nameof(TimestampedValuesDto.Timestamp), data.Timestamp); SetPropertyValueForStruct(ref boxedEntity, nameof(TimestampedValuesDto.DiscriminatorId), data.DiscriminatorId); return 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) { logger.LogError(ex.Message); } } 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) { logger.LogError(ex.Message); } } private void SetPropertyValueFromJson(ref object 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) { logger.LogError(ex.Message); } } private void SetPropertyValue(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) { logger.LogError(ex.Message); } } private PropertyInfo? GetPropertyInfo(string propertyName) { return PropertyCache.GetOrAdd(propertyName, name => entityType.GetProperty(name)); } }