| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- namespace Quadarax.Foundation.Core.Mapper
- {
- /// <summary>
- /// Mapping builder.
- /// </summary>
- /// <typeparam name="TFrom">The source type.</typeparam>
- /// <typeparam name="TTo">The destination type.</typeparam>
- public class LoomBuilder<TFrom, TTo>
- where TFrom : class
- where TTo : class, new()
- {
- private ParameterExpression sourceObj;
- private ParameterExpression destObj;
- private List<Expression> rules;
- private Loom loom;
- /// <summary>
- /// Initializes a new instance of this class.
- /// </summary>
- public LoomBuilder(Loom loom)
- {
- this.loom = loom;
- sourceObj = Expression.Variable(typeof(TFrom), "source");
- destObj = Expression.Variable(typeof(TTo), "dest");
- rules = new List<Expression>();
- }
- internal class ParameterReplacer : ExpressionVisitor
- {
- private ParameterExpression oldParam;
- private ParameterExpression newParam;
- /// <summary>
- /// Initializes a new instance of this class.
- /// </summary>
- public ParameterReplacer(ParameterExpression oldParam, ParameterExpression newParam)
- {
- this.oldParam = oldParam;
- this.newParam = newParam;
- }
- public static Expression Replace(Expression expr, ParameterExpression oldParam, ParameterExpression newParam)
- {
- return new ParameterReplacer(oldParam, newParam).Visit(expr);
- }
- protected override Expression VisitParameter(ParameterExpression node)
- {
- return node.Name != null && node.Name.Equals(oldParam.Name) ? newParam : node;
- }
- }
- public LoomBuilder<TFrom, TTo> Custom<T>(Expression<Func<TTo, T>> destSet, Expression<Func<TFrom, T>> sourceGet)
- {
- if (sourceGet == null) throw new ArgumentNullException("sourceGet");
- if (destSet == null) throw new ArgumentNullException("destSet");
- var rule = Expression.Assign(
- ParameterReplacer.Replace(destSet.Body, destSet.Parameters.Single(), destObj),
- ParameterReplacer.Replace(sourceGet.Body, sourceGet.Parameters.Single(), sourceObj));
- rules.Add(rule);
- return this;
- }
- public LoomBuilder<TFrom, TTo> PublicProperties()
- {
- var sourceProps = typeof(TFrom).GetProperties(BindingFlags.Public | BindingFlags.Instance);
- var destProps = typeof(TTo).GetProperties(BindingFlags.Public | BindingFlags.Instance);
- foreach (var sourceProp in sourceProps.Where(p => p.CanRead && p.GetGetMethod() != null))
- {
- var destProp = destProps.FirstOrDefault(p => p.Name == sourceProp.Name && p.CanWrite && p.GetSetMethod() != null);
- if (destProp != null)
- {
- if (destProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType))
- {
- // convert the source property's value to the destination type: required to assign into a Nullable<> destination property, and won't hurt in other cases
- rules.Add(Expression.Assign(Expression.Property(destObj, destProp), Expression.Convert(Expression.Property(sourceObj, sourceProp), destProp.PropertyType)));
- }
- else
- {
- // source may be Nullable<T>
- Type? sourceNullabeType = Nullable.GetUnderlyingType(sourceProp.PropertyType);
- if (sourceNullabeType != null && destProp.PropertyType.IsAssignableFrom(sourceNullabeType))
- {
- rules.Add(Expression.IfThen(
- Expression.Property(Expression.Property(sourceObj, sourceProp), "HasValue"),
- Expression.Assign(Expression.Property(destObj, destProp), Expression.Property(Expression.Property(sourceObj, sourceProp), "Value"))));
- }
- }
- }
- }
- return this;
- }
- public Func<TFrom, TTo?> Compile()
- {
- var parameters = new[] {sourceObj, destObj};
- var body = Expression.Block(rules);
- var map = Expression.Lambda<Action<TFrom, TTo>>(body, parameters).Compile();
- loom.Define(map);
- return loom.Map<TFrom, TTo>;
- }
- }
- }
|