231 lines
9.6 KiB
C#
231 lines
9.6 KiB
C#
|
#nullable enable
|
|||
|
using System;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Threading.Tasks;
|
|||
|
|
|||
|
using Swan.Lite.Reflection;
|
|||
|
using Swan.Mappers;
|
|||
|
using Swan.Reflection;
|
|||
|
|
|||
|
namespace Swan {
|
|||
|
/// <summary>
|
|||
|
/// Extension methods.
|
|||
|
/// </summary>
|
|||
|
public static partial class Extensions {
|
|||
|
/// <summary>
|
|||
|
/// 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.
|
|||
|
/// </summary>
|
|||
|
/// <typeparam name="T">The type of the source.</typeparam>
|
|||
|
/// <param name="source">The source.</param>
|
|||
|
/// <param name="target">The target.</param>
|
|||
|
/// <param name="ignoreProperties">The ignore properties.</param>
|
|||
|
/// <returns>
|
|||
|
/// Number of properties that was copied successful.
|
|||
|
/// </returns>
|
|||
|
public static Int32 CopyPropertiesTo<T>(this T source, Object? target, params String[]? ignoreProperties) where T : class => ObjectMapper.Copy(source, target, GetCopyableProperties(target), ignoreProperties);
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 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.
|
|||
|
/// </summary>
|
|||
|
/// <param name="source">The source.</param>
|
|||
|
/// <param name="target">The destination.</param>
|
|||
|
/// <param name="propertiesToCopy">Properties to copy.</param>
|
|||
|
/// <returns>
|
|||
|
/// Number of properties that were successfully copied.
|
|||
|
/// </returns>
|
|||
|
public static Int32 CopyOnlyPropertiesTo(this Object source, Object target, params String[]? propertiesToCopy) => ObjectMapper.Copy(source, target, propertiesToCopy);
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Copies the properties to new instance of T.
|
|||
|
/// </summary>
|
|||
|
/// <typeparam name="T">The new object type.</typeparam>
|
|||
|
/// <param name="source">The source.</param>
|
|||
|
/// <param name="ignoreProperties">The ignore properties.</param>
|
|||
|
/// <returns>
|
|||
|
/// The specified type with properties copied.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">source.</exception>
|
|||
|
public static T CopyPropertiesToNew<T>(this Object source, String[]? ignoreProperties = null) where T : class {
|
|||
|
if(source == null) {
|
|||
|
throw new ArgumentNullException(nameof(source));
|
|||
|
}
|
|||
|
|
|||
|
T target = Activator.CreateInstance<T>();
|
|||
|
_ = ObjectMapper.Copy(source, target, GetCopyableProperties(target), ignoreProperties);
|
|||
|
|
|||
|
return target;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Copies the only properties to new instance of T.
|
|||
|
/// </summary>
|
|||
|
/// <typeparam name="T">Object Type.</typeparam>
|
|||
|
/// <param name="source">The source.</param>
|
|||
|
/// <param name="propertiesToCopy">The properties to copy.</param>
|
|||
|
/// <returns>
|
|||
|
/// The specified type with properties copied.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">source.</exception>
|
|||
|
public static T CopyOnlyPropertiesToNew<T>(this Object source, params String[] propertiesToCopy) where T : class {
|
|||
|
if(source == null) {
|
|||
|
throw new ArgumentNullException(nameof(source));
|
|||
|
}
|
|||
|
|
|||
|
T target = Activator.CreateInstance<T>();
|
|||
|
_ = ObjectMapper.Copy(source, target, propertiesToCopy);
|
|||
|
|
|||
|
return target;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Iterates over the keys of the source and tries to write a compatible value to a public,
|
|||
|
/// instance, writable property in the destination.
|
|||
|
/// </summary>
|
|||
|
/// <param name="source">The source.</param>
|
|||
|
/// <param name="target">The target.</param>
|
|||
|
/// <param name="ignoreKeys">The ignore keys.</param>
|
|||
|
/// <returns>Number of properties that was copied successful.</returns>
|
|||
|
public static Int32 CopyKeyValuePairTo(this IDictionary<String, Object> source, Object? target, params String[] ignoreKeys) => source == null ? throw new ArgumentNullException(nameof(source)) : ObjectMapper.Copy(source, target, null, ignoreKeys);
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Iterates over the keys of the source and tries to write a compatible value to a public,
|
|||
|
/// instance, writable property in the destination.
|
|||
|
/// </summary>
|
|||
|
/// <typeparam name="T">Object Type.</typeparam>
|
|||
|
/// <param name="source">The source.</param>
|
|||
|
/// <param name="ignoreKeys">The ignore keys.</param>
|
|||
|
/// <returns>
|
|||
|
/// The specified type with properties copied.
|
|||
|
/// </returns>
|
|||
|
public static T CopyKeyValuePairToNew<T>(this IDictionary<String, Object> source, params String[] ignoreKeys) {
|
|||
|
if(source == null) {
|
|||
|
throw new ArgumentNullException(nameof(source));
|
|||
|
}
|
|||
|
|
|||
|
T target = Activator.CreateInstance<T>();
|
|||
|
_ = source.CopyKeyValuePairTo(target, ignoreKeys);
|
|||
|
return target;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Does the specified action.
|
|||
|
/// </summary>
|
|||
|
/// <param name="action">The action.</param>
|
|||
|
/// <param name="retryInterval">The retry interval.</param>
|
|||
|
/// <param name="retryCount">The retry count.</param>
|
|||
|
public static void Retry(this Action action, TimeSpan retryInterval = default, Int32 retryCount = 3) {
|
|||
|
if(action == null) {
|
|||
|
throw new ArgumentNullException(nameof(action));
|
|||
|
}
|
|||
|
|
|||
|
_ = Retry<Object?>(() => { action(); return null; }, retryInterval, retryCount);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Does the specified action.
|
|||
|
/// </summary>
|
|||
|
/// <typeparam name="T">The type of the source.</typeparam>
|
|||
|
/// <param name="action">The action.</param>
|
|||
|
/// <param name="retryInterval">The retry interval.</param>
|
|||
|
/// <param name="retryCount">The retry count.</param>
|
|||
|
/// <returns>
|
|||
|
/// The return value of the method that this delegate encapsulates.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">action.</exception>
|
|||
|
/// <exception cref="AggregateException">Represents one or many errors that occur during application execution.</exception>
|
|||
|
public static T Retry<T>(this Func<T> action, TimeSpan retryInterval = default, Int32 retryCount = 3) {
|
|||
|
if(action == null) {
|
|||
|
throw new ArgumentNullException(nameof(action));
|
|||
|
}
|
|||
|
|
|||
|
if(retryInterval == default) {
|
|||
|
retryInterval = TimeSpan.FromSeconds(1);
|
|||
|
}
|
|||
|
|
|||
|
global::System.Collections.Generic.List<global::System.Exception> exceptions = new List<Exception>();
|
|||
|
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the copyable properties.
|
|||
|
///
|
|||
|
/// If there is no properties with the attribute <c>AttributeCache</c> returns all the properties.
|
|||
|
/// </summary>
|
|||
|
/// <param name="this">The object.</param>
|
|||
|
/// <returns>
|
|||
|
/// Array of properties.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">model.</exception>
|
|||
|
/// <seealso cref="AttributeCache"/>
|
|||
|
public static IEnumerable<String> GetCopyableProperties(this Object? @this) {
|
|||
|
if(@this == null) {
|
|||
|
throw new ArgumentNullException(nameof(@this));
|
|||
|
}
|
|||
|
|
|||
|
global::System.Collections.Generic.IEnumerable<global::System.Reflection.PropertyInfo> collection = PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties(@this.GetType(), true);
|
|||
|
|
|||
|
global::System.Collections.Generic.IEnumerable<global::System.String> properties = collection.Select(x => new {
|
|||
|
x.Name,
|
|||
|
HasAttribute = AttributeCache.DefaultCache.Value.RetrieveOne<CopyableAttribute>(x) != null,
|
|||
|
}).Where(x => x.HasAttribute).Select(x => x.Name);
|
|||
|
|
|||
|
return properties.Any() ? properties : collection.Select(x => x.Name);
|
|||
|
}
|
|||
|
|
|||
|
internal static void CreateTarget(this Object source, Type targetType, Boolean includeNonPublic, ref Object? target) {
|
|||
|
switch(source) {
|
|||
|
// do nothing. Simply skip creation
|
|||
|
case String _:
|
|||
|
break;
|
|||
|
// When using arrays, there is no default constructor, attempt to build a compatible array
|
|||
|
case IList sourceObjectList when targetType.IsArray:
|
|||
|
Type? elementType = targetType.GetElementType();
|
|||
|
|
|||
|
if(elementType != null) {
|
|||
|
target = Array.CreateInstance(elementType, sourceObjectList.Count);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
default:
|
|||
|
IEnumerable<Tuple<System.Reflection.ConstructorInfo, System.Reflection.ParameterInfo[]>> constructors = ConstructorTypeCache.DefaultCache.Value.RetrieveAllConstructors(targetType, includeNonPublic);
|
|||
|
|
|||
|
// Try to check if empty constructor is available
|
|||
|
if(constructors.Any(x => x.Item2.Length == 0)) {
|
|||
|
target = Activator.CreateInstance(targetType, includeNonPublic);
|
|||
|
} else {
|
|||
|
Tuple<System.Reflection.ConstructorInfo, System.Reflection.ParameterInfo[]> firstCtor = constructors.OrderBy(x => x.Item2.Length).FirstOrDefault();
|
|||
|
|
|||
|
target = Activator.CreateInstance(targetType, firstCtor?.Item2.Select(arg => arg.GetType().GetDefault()).ToArray());
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
internal static String GetNameWithCase(this String name, JsonSerializerCase jsonSerializerCase) => jsonSerializerCase switch
|
|||
|
{
|
|||
|
JsonSerializerCase.PascalCase => Char.ToUpperInvariant(name[0]) + name.Substring(1),
|
|||
|
JsonSerializerCase.CamelCase => Char.ToLowerInvariant(name[0]) + name.Substring(1),
|
|||
|
JsonSerializerCase.None => name,
|
|||
|
_ => throw new ArgumentOutOfRangeException(nameof(jsonSerializerCase), jsonSerializerCase, null)
|
|||
|
};
|
|||
|
}
|
|||
|
}
|