public RecursiveObjectCopier(Expression <Func <S, D> > specifications = null) { //if (specifications == null) throw new ArgumentException(nameof(specifications)); changes = new ObjectChangesRegister(null, true); originalSpecifications = specifications; specifications = ProjectionExpression <S> .BuildExpression(specifications, null, changes); FullExpression = specifications; transform = specifications.Compile(); bool hasIEnumerables; paths = changes.ComputePaths(out hasIEnumerables); HasIEnumerables = hasIEnumerables; changes.IndexProperties(); }
internal static LambdaExpression BuildInternalExpression(LambdaExpression exp, Type filter = null, ObjectChangesRegister changesRegister = null) { ParameterExpression parameterExpression = exp.Parameters.First(); if (exp.Body.NodeType == ExpressionType.MemberInit) { var bindings = (exp.Body as MemberInitExpression).Bindings; if (bindings == null || bindings.Count == 0) { var res = Expression.Lambda( createMemberInit(parameterExpression, exp.ReturnType, parameterExpression.Type, filter, changesRegister), parameterExpression); var key = GetCacheKey(exp.ReturnType, parameterExpression.Type, filter); try { ExpressionCache.TryAdd(key, res); } catch { } return(res); } } var pres = processTreeRec(exp.Body, parameterExpression, filter, null, null, null, changesRegister); return(Expression.Lambda(pres, parameterExpression)); }
public static Expression <Func <TSource, TDest> > BuildExpression <TDest>(Expression <Func <TSource, TDest> > custom, Type filter = null, ObjectChangesRegister changesRegister = null) { ParameterExpression parameterExpression = custom == null?Expression.Parameter(typeof(TSource), "src") : custom.Parameters.First(); Expression pres; if (custom == null) { pres = createMemberInit <TDest>(parameterExpression, filter, changesRegister); } else { pres = processTreeRec(custom.Body, parameterExpression, filter, null, null, null, changesRegister); } var expression = Expression.Lambda <Func <TSource, TDest> >(pres, parameterExpression); if (custom == null) { var key = GetCacheKey <TDest>(); try { ExpressionCache.TryAdd(key, expression); } catch { } } return(expression); }
private static Expression processTreeRec( Expression node, ParameterExpression parameterExpression, Type filter = null, string prefix = null, Stack <PropertyInfo> sourceProperties = null, PropertyInfo currProperty = null, ObjectChangesRegister changesRegister = null) { if (node.NodeType == ExpressionType.MemberInit) { if (changesRegister != null) { changesRegister.MoveToComplex(); } var currType = sourceProperties != null && sourceProperties.Count > 0 ? sourceProperties.Peek().PropertyType : parameterExpression.Type; string newPrefix = prefix == null ? currProperty?.Name : prefix + currProperty?.Name;; var sourceProperty = newPrefix == null ? null : currType.GetProperty(newPrefix); if (currProperty != null && currProperty.PropertyType.GetTypeInfo().IsInterface) { filter = currProperty.PropertyType; } else { filter = null; } if (sourceProperty != null) { if (sourceProperties == null) { sourceProperties = new Stack <PropertyInfo>(); } sourceProperties.Push(sourceProperty); var res = completeMemberInit(node as MemberInitExpression, parameterExpression, filter, null, sourceProperties, changesRegister); sourceProperties.Pop(); if (sourceProperties.Count == 0) { sourceProperties = null; } return(res); } else { return(completeMemberInit(node as MemberInitExpression, parameterExpression, filter, newPrefix, sourceProperties, changesRegister)); } } else if (node.NodeType == ExpressionType.Conditional) { var cond = node as ConditionalExpression; var ifTrue = processTreeRec(cond.IfTrue, parameterExpression, filter, prefix, sourceProperties, currProperty, changesRegister); var ifFalse = processTreeRec(cond.IfFalse, parameterExpression, filter, prefix, sourceProperties, currProperty, changesRegister); if (ifTrue == cond.IfTrue && ifFalse == cond.IfFalse) { return(node); } return(Expression.Condition(cond.Test, ifTrue, ifFalse)); } else if (node.NodeType == ExpressionType.Convert) { var conv = node as UnaryExpression; var toConvert = processTreeRec(conv.Operand, parameterExpression, filter, prefix, sourceProperties, currProperty, changesRegister); if (toConvert == conv.Operand) { return(node); } return(Expression.Convert(toConvert, conv.Type, conv.Method)); } else if (node.NodeType == ExpressionType.ConvertChecked) { var conv = node as UnaryExpression; var toConvert = processTreeRec(conv.Operand, parameterExpression, filter, prefix, sourceProperties, currProperty, changesRegister); if (toConvert == conv.Operand) { return(node); } return(Expression.ConvertChecked(toConvert, conv.Type, conv.Method)); } else if (node.NodeType == ExpressionType.New) { if (changesRegister != null) { changesRegister.MoveToComplex(); } return(node); } else { return(node); } }
private static MemberInitExpression createMemberInit(ParameterExpression parameterExpression, Type destination, Type source = null, Type filter = null, ObjectChangesRegister changesRegister = null) { var internalBindings = BuildBindings(destination, source, filter); IEnumerable <MemberAssignment> bindings; if (changesRegister != null) { changesRegister.MoveToComplex(); var lbindings = new List <MemberAssignment>(); if (internalBindings != null) { foreach (var binding in internalBindings) { var bind = BuildBinding(parameterExpression, binding, null, true); lbindings.Add(bind); changesRegister .AddChange(new ObjectChangesRegister(binding.Destination as PropertyInfo, false, null, bind.Expression)); } } bindings = lbindings; } else { bindings = internalBindings .Select(m => BuildBinding(parameterExpression, m, null, changesRegister != null)); } return(Expression.MemberInit(Expression.New(destination), bindings)); }
private static MemberInitExpression completeMemberInit( MemberInitExpression node, ParameterExpression parameterExpression, Type filter = null, string prefix = null, Stack <PropertyInfo> sourceProperties = null, ObjectChangesRegister changesRegister = null) { var customAssignements = node.Bindings.Where(m => m.BindingType == MemberBindingType.Assignment).Select(m => m as MemberAssignment).ToList(); var assignedProperties = customAssignements.Select(m => m.Member.Name).ToList(); var internalProjections = customAssignements.Where(m => getNestedSelect(m.Expression) != null); var otherAssignements = customAssignements.Except(internalProjections); List <MemberAssignment> modifiedAssignements = null; List <MemberAssignment> modifiedAssignementsOld = null; if (internalProjections != null) { foreach (var projection in internalProjections) { var select = getNestedSelect(projection.Expression); var exp = select.Arguments[1] as LambdaExpression; var originalCollection = select.Arguments[0] as MemberExpression; if (exp != null) { Type childFilter = null; var propertyInfo = (projection.Member as PropertyInfo); TypeInfo childType = propertyInfo.PropertyType.GetTypeInfo(); if (childType.IsGenericType && childType.GenericTypeArguments.Length == 1 && childType.GenericTypeArguments[0].GetTypeInfo().IsInterface) { childFilter = childType.GenericTypeArguments[0]; } var childChangesRegister = changesRegister == null ? null: new ObjectChangesRegister(propertyInfo, true, childType.GenericTypeArguments[0], null, originalCollection.Member as PropertyInfo); var newExpression = copyCallChain(projection.Expression as MethodCallExpression, select, Expression.TypeAs( Expression.Call(select.Method, originalCollection, BuildInternalExpression(exp, childFilter, childChangesRegister)), select.Method.ReturnType) ); var newAssignement = changesRegister == null?Expression.Bind(projection.Member, newExpression) : Expression.Bind(projection.Member, Expression.Condition(Expression.Equal(copyMemberAccesses(select.Arguments[0]), Expression.Constant(null)), Expression.Constant(null, newExpression.Type), newExpression)); if (changesRegister != null) { childChangesRegister.SetExpression(newExpression); changesRegister.AddChange(childChangesRegister); } if (modifiedAssignements == null) { modifiedAssignements = new List <MemberAssignment>(); modifiedAssignementsOld = new List <MemberAssignment>(); } modifiedAssignements.Add(newAssignement); modifiedAssignementsOld.Add(projection); } } } if (otherAssignements != null) { foreach (var assignement in otherAssignements) { var newChange = changesRegister == null ? null : new ObjectChangesRegister(assignement.Member as PropertyInfo, false, null, null); var newNode = processTreeRec(assignement.Expression, parameterExpression, filter, prefix, sourceProperties, assignement.Member as PropertyInfo, newChange); if (changesRegister != null) { newChange.SetExpression(newNode); changesRegister.AddChange(newChange); } if (newNode != assignement.Expression) { var newAssignement = Expression.Bind(assignement.Member, newNode); if (modifiedAssignements == null) { modifiedAssignements = new List <MemberAssignment>(); modifiedAssignementsOld = new List <MemberAssignment>(); } modifiedAssignements.Add(newAssignement); modifiedAssignementsOld.Add(assignement); } } } var innerBindings = BuildBindings(node.NewExpression.Type, sourceProperties != null && sourceProperties.Count > 0 ? sourceProperties.Peek().PropertyType : parameterExpression.Type, filter, prefix) .Where(m => !assignedProperties.Contains(m.Destination.Name)); IEnumerable <MemberAssignment> bindings; if (innerBindings != null && changesRegister != null) { var lbindings = new List <MemberAssignment>(); foreach (var binding in innerBindings) { var bind = BuildBinding(parameterExpression, binding, sourceProperties, true); lbindings.Add(bind); changesRegister .AddChange(new ObjectChangesRegister(binding.Destination as PropertyInfo, false, null, bind.Expression)); } bindings = lbindings.Union(customAssignements); } else { bindings = innerBindings .Select(m => BuildBinding(parameterExpression, m, sourceProperties, changesRegister != null)) .Union(customAssignements); } if (modifiedAssignements != null && modifiedAssignements.Count > 0) { bindings = bindings.Except(modifiedAssignementsOld) .Union(modifiedAssignements); } return(Expression.MemberInit(node.NewExpression, bindings)); }