using Unosquare.Swan.Attributes; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading.Tasks; namespace Unosquare.Swan { /// /// Extension methods. /// public static partial class Extensions { /// /// Iterates over the public, instance, readable properties of the source and /// tries to write a compatible value to a public, instance, writable property in the destination. /// /// The type of the source. /// The source. /// The target. /// Number of properties that was copied successful. public static Int32 CopyPropertiesTo(this T source, Object target) where T : class { IEnumerable copyable = GetCopyableProperties(target); return copyable.Any() ? CopyOnlyPropertiesTo(source, target, copyable.ToArray()) : CopyPropertiesTo(source, target, null); } /// /// Iterates over the public, instance, readable properties of the source and /// tries to write a compatible value to a public, instance, writable property in the destination. /// /// The source. /// The destination. /// The ignore properties. /// /// Number of properties that were successfully copied. /// public static Int32 CopyPropertiesTo(this Object source, Object target, String[] ignoreProperties = null) => Components.ObjectMapper.Copy(source, target, null, ignoreProperties); /// /// Iterates over the public, instance, readable properties of the source and /// tries to write a compatible value to a public, instance, writable property in the destination. /// /// The type of the source. /// The source. /// The target. /// Number of properties that was copied successful. public static Int32 CopyOnlyPropertiesTo(this T source, Object target) where T : class => CopyOnlyPropertiesTo(source, target, null); /// /// Iterates over the public, instance, readable properties of the source and /// tries to write a compatible value to a public, instance, writable property in the destination. /// /// The source. /// The destination. /// Properties to copy. /// /// Number of properties that were successfully copied. /// public static Int32 CopyOnlyPropertiesTo(this Object source, Object target, String[] propertiesToCopy) => Components.ObjectMapper.Copy(source, target, propertiesToCopy); /// /// Copies the properties to new instance of T. /// /// The new object type. /// The source. /// The ignore properties. /// /// The specified type with properties copied. /// /// source. public static T DeepClone(this T source, String[] ignoreProperties = null) where T : class => source.CopyPropertiesToNew(ignoreProperties); /// /// Copies the properties to new instance of T. /// /// The new object type. /// The source. /// The ignore properties. /// /// The specified type with properties copied. /// /// source. public static T CopyPropertiesToNew(this Object source, String[] ignoreProperties = null) where T : class { if(source == null) { throw new ArgumentNullException(nameof(source)); } T target = Activator.CreateInstance(); IEnumerable copyable = target.GetCopyableProperties(); _ = copyable.Any() ? source.CopyOnlyPropertiesTo(target, copyable.ToArray()) : source.CopyPropertiesTo(target, ignoreProperties); return target; } /// /// Copies the only properties to new instance of T. /// /// Object Type. /// The source. /// The properties to copy. /// /// The specified type with properties copied. /// /// source. public static T CopyOnlyPropertiesToNew(this Object source, String[] propertiesToCopy) where T : class { if(source == null) { throw new ArgumentNullException(nameof(source)); } T target = Activator.CreateInstance(); _ = source.CopyOnlyPropertiesTo(target, propertiesToCopy); return target; } /// /// Iterates over the keys of the source and tries to write a compatible value to a public, /// instance, writable property in the destination. /// /// The source. /// The target. /// The ignore keys. /// Number of properties that was copied successful. public static Int32 CopyKeyValuePairTo( this IDictionary source, Object target, String[] ignoreKeys = null) => Components.ObjectMapper.Copy(source, target, null, ignoreKeys); /// /// Measures the elapsed time of the given action as a TimeSpan /// This method uses a high precision Stopwatch. /// /// The target. /// /// A time interval that represents a specified time, where the specification is in units of ticks. /// /// target. public static TimeSpan Benchmark(this Action target) { if(target == null) { throw new ArgumentNullException(nameof(target)); } Stopwatch sw = new Stopwatch(); try { sw.Start(); target.Invoke(); } catch { // swallow } finally { sw.Stop(); } return TimeSpan.FromTicks(sw.ElapsedTicks); } /// /// Does the specified action. /// /// The action. /// The retry interval. /// The retry count. public static void Retry( this Action action, TimeSpan retryInterval = default, Int32 retryCount = 3) { if(action == null) { throw new ArgumentNullException(nameof(action)); } _ = Retry(() => { action(); return null; }, retryInterval, retryCount); } /// /// Does the specified action. /// /// The type of the source. /// The action. /// The retry interval. /// The retry count. /// /// The return value of the method that this delegate encapsulates. /// /// action. /// Represents one or many errors that occur during application execution. public static T Retry( this Func action, TimeSpan retryInterval = default, Int32 retryCount = 3) { if(action == null) { throw new ArgumentNullException(nameof(action)); } if(retryInterval == default) { retryInterval = TimeSpan.FromSeconds(1); } List exceptions = new List(); for(Int32 retry = 0; retry < retryCount; retry++) { try { if(retry > 0) { Task.Delay(retryInterval).Wait(); } return action(); } catch(Exception ex) { exceptions.Add(ex); } } throw new AggregateException(exceptions); } /// /// Retrieves the exception message, plus all the inner exception messages separated by new lines. /// /// The ex. /// The prior message. /// A that represents this instance. public static String ExceptionMessage(this Exception ex, String priorMessage = "") { while(true) { if(ex == null) { throw new ArgumentNullException(nameof(ex)); } String fullMessage = String.IsNullOrWhiteSpace(priorMessage) ? ex.Message : priorMessage + "\r\n" + ex.Message; if(String.IsNullOrWhiteSpace(ex.InnerException?.Message)) { return fullMessage; } ex = ex.InnerException; priorMessage = fullMessage; } } /// /// Gets the copyable properties. /// /// The object. /// /// Array of properties. /// /// model. public static IEnumerable GetCopyableProperties(this Object obj) { if(obj == null) { throw new ArgumentNullException(nameof(obj)); } return Runtime.PropertyTypeCache .RetrieveAllProperties(obj.GetType(), true) .Select(x => new { x.Name, HasAttribute = Runtime.AttributeCache.RetrieveOne(x) != null }) .Where(x => x.HasAttribute) .Select(x => x.Name); } /// /// Returns true if the object is valid. /// /// The object. /// /// true if the specified model is valid; otherwise, false. /// public static Boolean IsValid(this Object obj) => Runtime.ObjectValidator.IsValid(obj); internal static void CreateTarget( this Object source, Type targetType, Boolean includeNonPublic, ref Object target) { switch(source) { case String _: break; // do nothing. Simply skip creation case IList sourceObjectList when targetType.IsArray: // When using arrays, there is no default constructor, attempt to build a compatible array Type elementType = targetType.GetElementType(); if(elementType != null) { target = Array.CreateInstance(elementType, sourceObjectList.Count); } break; default: target = Activator.CreateInstance(targetType, includeNonPublic); break; } } } }