diff --git a/zero.Core/Blueprints/Blueprint.cs b/zero.Core/Blueprints/Blueprint.cs index 02061b48..3642883a 100644 --- a/zero.Core/Blueprints/Blueprint.cs +++ b/zero.Core/Blueprints/Blueprint.cs @@ -15,6 +15,8 @@ namespace zero.Core.Blueprints { public List> UnlockedFields { get; private set; } = new(); + protected static string[] DefaultFields { get; set; } = new[] { "name", "alias", "key", "sort", "isActive", "hash", "languageId", "createdById", "createdDate", "lastModifiedById", "lastModifiedDate", "blueprint" }; + public Blueprint() : base(typeof(T)) { @@ -40,8 +42,8 @@ namespace zero.Core.Blueprints } // copy all properties which are synced from the blueprint to the model - ObjectTraverser.CopyProperties(blueprint, model, model.Blueprint.Desync); ApplyDefaults(blueprint, model); + ObjectCloner.CopyProperties(blueprint, model, DefaultFields.Union(model.Blueprint.Desync).ToArray()); return model; } diff --git a/zero.Core/Utils/ObjectCloner.cs b/zero.Core/Utils/ObjectCloner.cs new file mode 100644 index 00000000..0c76c979 --- /dev/null +++ b/zero.Core/Utils/ObjectCloner.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace zero.Core.Utils +{ + public class ObjectCloner + { + const string SEPARATOR = "."; + + static Type STRING_TYPE = typeof(string); + + static ConcurrentDictionary> PublicPropertiesPerType { get; set; } = new(); + + + + public static T CopyProperties(T source, T target, params string[] exceptions) + { + return CopyPropertiesInternal(source, target, null, exceptions); + } + + + static T CopyPropertiesInternal(T source, T target, string keyPrefix = null, params string[] exceptions) + { + Type type = source.GetType(); + + if (!PublicPropertiesPerType.TryGetValue(type.FullName, out IEnumerable properties)) + { + properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.CanWrite); + PublicPropertiesPerType.TryAdd(type.FullName, properties); + } + + foreach (PropertyInfo property in properties) + { + string propertyNameWithPrefix = CombineKey(SEPARATOR, keyPrefix, property.Name); + + if (exceptions.Contains(propertyNameWithPrefix, StringComparer.InvariantCultureIgnoreCase)) + { + continue; + } + + object propertyValue = property.GetValue(source, null); + + if (property.PropertyType.IsValueType || property.PropertyType.IsEnum || property.PropertyType.Equals(STRING_TYPE) || propertyValue == null || propertyValue is IEnumerable) + { + Console.WriteLine("copy: " + propertyNameWithPrefix); + property.SetValue(target, propertyValue, null); + } + else if (property.PropertyType.IsClass) + { + string[] nestedExceptions = exceptions.Where(x => x.StartsWith(propertyNameWithPrefix, StringComparison.InvariantCultureIgnoreCase)).ToArray(); + property.SetValue(target, CopyPropertiesInternal(propertyValue, property.GetValue(target, null), propertyNameWithPrefix, nestedExceptions), null); + } + } + + return target; + } + + + private static string CombineKey(string sep, string key1, string key2) + { + if (String.IsNullOrEmpty(key1)) + { + return key2; + } + return String.Join(sep, key1, key2); + } + } +} diff --git a/zero.Core/Utils/ObjectTraverser.cs b/zero.Core/Utils/ObjectTraverser.cs index 9f1c9abc..fd5dbafe 100644 --- a/zero.Core/Utils/ObjectTraverser.cs +++ b/zero.Core/Utils/ObjectTraverser.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -238,36 +239,6 @@ namespace zero.Core.Utils } - public static T CopyProperties(T source, T target, params string[] exceptions) - { - Type sourceType = source.GetType(); - Type stringType = typeof(System.String); - - foreach (PropertyInfo sourceProperty in sourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) - { - Console.WriteLine("key: " + sourceProperty.Name); - - if (!sourceProperty.CanWrite || exceptions.Contains(sourceProperty.Name, StringComparer.InvariantCultureIgnoreCase)) - { - continue; - } - - object sourcePropertyValue = sourceProperty.GetValue(source, null); - - if (sourceProperty.PropertyType.IsValueType || sourceProperty.PropertyType.IsEnum || sourceProperty.PropertyType.Equals(stringType) || sourcePropertyValue == null || sourcePropertyValue is ICollection) - { - sourceProperty.SetValue(target, sourcePropertyValue, null); - } - else if (sourceProperty.PropertyType.IsClass) - { - sourceProperty.SetValue(target, CopyProperties(sourcePropertyValue, sourceProperty.GetValue(target, null)), null); - } - } - - return target; - } - - private static string CombineKey(string sep, string key1, string key2) { if (String.IsNullOrEmpty(key1))