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(TimestampedSetDto 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(TimestampedSetDto data) { return DeserializeTimeStampedData(data)!; } public T DeserializeTimeStampedData(TimestampedSetDto data) { if (entityType.IsValueType) return MapStruct(data); else return MapClass(data); } private T MapClass(TimestampedSetDto data) { var entity = (T)RuntimeHelpers.GetUninitializedObject(typeof(T)); foreach (var (propertyName, value) in data.Set) { if (value is JsonElement jsonElement) SetPropertyValueFromJson(ref entity, propertyName, jsonElement); } SetPropertyValue(ref entity, "Timestamp", data.Timestamp); return entity; } private T MapStruct(TimestampedSetDto data) { var entity = Activator.CreateInstance(); object boxedEntity = entity!; foreach (var (propertyName, value) in data.Set) { 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)); } }