65 lines
3.1 KiB
C#
65 lines
3.1 KiB
C#
using System.Dynamic;
|
|
using System.Reflection;
|
|
|
|
namespace adas_core.Domain.Utils;
|
|
|
|
public static class Mapper<T>
|
|
// We can only use reference types
|
|
where T : class
|
|
{
|
|
private static readonly Dictionary<string, PropertyInfo> PropertyMap;
|
|
|
|
static Mapper()
|
|
{
|
|
// At this point we can convert each
|
|
// property name to lower case so we avoid
|
|
// creating a new string more than once.
|
|
PropertyMap =
|
|
typeof(T)
|
|
.GetProperties()
|
|
.ToDictionary(
|
|
p => p.Name.ToLower(),
|
|
p => p
|
|
);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Maps properties from an <see cref="ExpandoObject"/> source to a strongly-typed destination object using a pre-defined property map.
|
|
/// Property lookups are case-insensitive. Null values are only assigned to reference types or <see cref="Nullable{T}"/> types; assigning null to a non-nullable value type throws. When a source value's type does not match the destination property's type, the value is converted using <see cref="Convert.ToDouble(object)"/>.
|
|
/// </summary>
|
|
/// <param name="source">The source <see cref="ExpandoObject"/> whose key-value pairs will be mapped onto the destination.</param>
|
|
/// <param name="destination">The target object that will receive the mapped property values.</param>
|
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="source"/> or <paramref name="destination"/> is null.</exception>
|
|
/// <exception cref="ArgumentException">Thrown when a source value is null but the corresponding destination property is a value type that is not <see cref="Nullable{T}"/>.</exception>
|
|
public static void Map(ExpandoObject source, T destination)
|
|
{
|
|
// Might as well take care of null references early.
|
|
if (source == null) throw new ArgumentNullException(nameof(source));
|
|
|
|
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
|
|
|
// By iterating the KeyValuePair<string, object> of
|
|
// source we can avoid manually searching the keys of
|
|
// source as we see in your original code.
|
|
foreach (var kv in source)
|
|
if (PropertyMap.TryGetValue(kv.Key.ToLower(), out var p))
|
|
{
|
|
var propType = p.PropertyType;
|
|
if (kv.Value == null)
|
|
{
|
|
if (propType is { IsByRef: false } && propType.Name != "Nullable`1")
|
|
// Throw if type is a value type
|
|
// but not Nullable<>
|
|
throw new ArgumentException("not nullable");
|
|
}
|
|
else if (kv.Value.GetType() != propType)
|
|
{
|
|
// You could make this a bit less strict
|
|
// but I don't recommend it.
|
|
p.SetValue(destination, Convert.ToDouble(kv.Value), null);
|
|
}
|
|
|
|
p.SetValue(destination, kv.Value, null);
|
|
}
|
|
}
|
|
} |