protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { if (destination.Type.IsArray) { if (source.Type.IsArray && source.Type.GetElementType() == destination.Type.GetElementType()) { //Array.Copy(src, 0, dest, 0, src.Length) var method = typeof (Array).GetMethod("Copy", new[] {typeof (Array), typeof (int), typeof (Array), typeof (int), typeof (int)}); return Expression.Call(method, source, Expression.Constant(0), destination, Expression.Constant(0), Expression.ArrayLength(source)); } else return CreateArraySet(source, destination, arg); } else { var destinationElementType = destination.Type.ExtractCollectionType(); var listType = destination.Type.GetGenericEnumerableType() != null ? typeof(ICollection<>).MakeGenericType(destinationElementType) : typeof(IList); var tmp = Expression.Variable(listType); var assign = ExpressionEx.Assign(tmp, destination); //convert to list type var set = CreateListSet(source, tmp, arg); return Expression.Block(new[] {tmp}, assign, set); } }
protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { //### !IgnoreNullValues //dest.Prop1 = convert(src.Prop1); //dest.Prop2 = convert(src.Prop2); //### IgnoreNullValues //if (src.Prop1 != null) // dest.Prop1 = convert(src.Prop1); //if (src.Prop2 != null) // dest.Prop2 = convert(src.Prop2); var properties = CreateAdapterModel(source, destination, arg); var lines = new List<Expression>(); foreach (var property in properties) { var getter = CreateAdaptExpression(property.Getter, property.Setter.Type, arg); Expression itemAssign = Expression.Assign(property.Setter, getter); if (arg.Settings.IgnoreNullValues == true && (!property.Getter.Type.GetTypeInfo().IsValueType || property.Getter.Type.IsNullable())) { var condition = Expression.NotEqual(property.Getter, Expression.Constant(null, property.Getter.Type)); itemAssign = Expression.IfThen(condition, itemAssign); } lines.Add(itemAssign); } return Expression.Block(lines); }
public LambdaExpression CreateAdaptToTargetFunc(CompileArgument arg) { var p = Expression.Parameter(arg.SourceType); var p2 = Expression.Parameter(arg.DestinationType); var body = CreateExpressionBody(p, p2, arg); return Expression.Lambda(body, p, p2); }
protected override Expression CreateExpressionBody(Expression source, Expression destination, CompileArgument arg) { Expression convert = source; var sourceType = arg.SourceType; var destinationType = arg.DestinationType; if (sourceType != destinationType) { if (sourceType.IsNullable()) { convert = Expression.Convert(convert, sourceType.GetGenericArguments()[0]); } convert = ReflectionUtils.BuildUnderlyingTypeConvertExpression(convert, sourceType, destinationType); if (convert.Type != destinationType) convert = Expression.Convert(convert, destinationType); if (arg.MapType != MapType.Projection && (!arg.SourceType.GetTypeInfo().IsValueType || arg.SourceType.IsNullable())) { //src == null ? default(TDestination) : convert(src) var compareNull = Expression.Equal(source, Expression.Constant(null, sourceType)); convert = Expression.Condition(compareNull, Expression.Constant(destinationType.GetDefault(), destinationType), convert); } } return convert; }
protected override bool CanInline(Expression source, Expression destination, CompileArgument arg) { if (!base.CanInline(source, destination, arg)) return false; if (arg.MapType != MapType.Projection && arg.Settings.IgnoreNullValues == true) return false; return true; }
protected override Expression CreateExpressionBody(Expression source, Expression destination, CompileArgument arg) { if (arg.IsInvalidRequiredExplicitMapping) { throw new InvalidOperationException( $"Implicit mapping is not allowed (check GlobalSettings.RequireExplicitMapping) and no configuration exists for the following mapping: TSource: {arg.SourceType} TDestination: {arg.DestinationType}"); } return base.CreateExpressionBody(source, destination, arg); }
protected ClassMapping CreateClassConverter(Expression source, Expression destination, CompileArgument arg) { var classModel = GetClassModel(arg.DestinationType); var destinationMembers = classModel.Members; var unmappedDestinationMembers = new List<string>(); var properties = new List<MemberMapping>(); foreach (var destinationMember in destinationMembers) { if (ProcessIgnores(arg.Settings, destinationMember)) continue; var member = destinationMember; var getter = arg.Settings.ValueAccessingStrategies .Select(fn => fn(source, member, arg)) .FirstOrDefault(result => result != null); if (getter != null) { var propertyModel = new MemberMapping { Getter = getter, Setter = destinationMember.GetExpression(destination), SetterInfo = destinationMember.Info, }; properties.Add(propertyModel); } else if (classModel.ConstructorInfo != null) { var propertyModel = new MemberMapping { Getter = null, Setter = destinationMember.GetExpression(destination), SetterInfo = destinationMember.Info, }; properties.Add(propertyModel); } else if (destinationMember.SetterModifier != AccessModifier.None) { unmappedDestinationMembers.Add(destinationMember.Name); } } if (arg.Context.Config.RequireDestinationMemberSource && unmappedDestinationMembers.Count > 0) { throw new ArgumentOutOfRangeException($"The following members of destination class {arg.DestinationType} do not have a corresponding source member mapped or ignored:{string.Join(",", unmappedDestinationMembers)}"); } return new ClassMapping { ConstructorInfo = classModel.ConstructorInfo, Members = properties, }; }
private static IEnumerable<PropertyModel> CreateAdapterModel(Expression source, Expression destination, CompileArgument arg) { Type sourceType = source.Type; var destinationMembers = arg.DestinationType.GetPublicFieldsAndProperties(allowNoSetter: false); var unmappedDestinationMembers = new List<string>(); var properties = new List<PropertyModel>(); for (int i = 0; i < destinationMembers.Count; i++) { MemberInfo destinationMember = destinationMembers[i]; bool isProperty = destinationMember is PropertyInfo; if (ProcessIgnores(arg.Settings, destinationMember)) continue; if (ProcessCustomResolvers(source, destination, arg.Settings, destinationMember, properties)) continue; MemberInfo sourceMember = ReflectionUtils.GetPublicFieldOrProperty(sourceType, isProperty, destinationMember.Name); if (sourceMember != null) { var propertyModel = new PropertyModel { ConvertType = 1, Getter = sourceMember is PropertyInfo ? Expression.Property(source, (PropertyInfo)sourceMember) : Expression.Field(source, (FieldInfo)sourceMember), Setter = destinationMember is PropertyInfo ? Expression.Property(destination, (PropertyInfo) destinationMember) : Expression.Field(destination, (FieldInfo) destinationMember), SetterProperty = destinationMember, }; properties.Add(propertyModel); } else { if (FlattenMethod(source, destination, destinationMember, properties)) continue; if (FlattenClass(source, destination, destinationMember, properties, arg.MapType == MapType.Projection)) continue; if (destinationMember.HasPublicSetter()) { unmappedDestinationMembers.Add(destinationMember.Name); } } } if (arg.Context.Config.RequireDestinationMemberSource && unmappedDestinationMembers.Count > 0) { throw new ArgumentOutOfRangeException($"The following members of destination class {arg.DestinationType} do not have a corresponding source member mapped or ignored:{string.Join(",", unmappedDestinationMembers)}"); } return properties; }
protected virtual bool CanInline(Expression source, Expression destination, CompileArgument arg) { if (destination != null) return false; if (arg.Settings.ConstructUsing != null && arg.Settings.ConstructUsing.Body.NodeType != ExpressionType.New) return false; if (arg.Settings.PreserveReference == true && arg.MapType != MapType.Projection && !arg.SourceType.GetTypeInfo().IsValueType && !arg.DestinationType.GetTypeInfo().IsValueType) return false; return true; }
protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { //### !IgnoreNullValues //dict.Add("Prop1", convert(src.Prop1)); //dict.Add("Prop2", convert(src.Prop2)); //### IgnoreNullValues //if (src.Prop1 != null) // dict.Add("Prop1", convert(src.Prop1)); //if (src.Prop2 != null) // dict.Add("Prop2", convert(src.Prop2)); var dictType = destination.Type.GetDictionaryType(); var valueType = dictType.GetGenericArguments()[1]; var indexer = dictType.GetProperties().First(item => item.GetIndexParameters().Length > 0); var lines = new List<Expression>(); var dict = Expression.Variable(dictType); lines.Add(Expression.Assign(dict, destination)); MethodInfo setMethod = null; var strategy = arg.Settings.NameMatchingStrategy; if (arg.MapType == MapType.MapToTarget && strategy.DestinationMemberNameConverter != NameMatchingStrategy.Identity) { var args = dictType.GetGenericArguments(); setMethod = typeof (Extensions).GetMethods().First(m => m.Name == "FlexibleSet") .MakeGenericMethod(args[1]); } var properties = source.Type.GetPublicFieldsAndProperties(); foreach (var property in properties) { var getter = property.GetExpression(source); var value = CreateAdaptExpression(getter, valueType, arg); var sourceMemberName = strategy.SourceMemberNameConverter(property.Name); Expression key = Expression.Constant(sourceMemberName); var itemSet = setMethod != null ? (Expression)Expression.Call(setMethod, dict, key, Expression.Constant(strategy.DestinationMemberNameConverter), value) : Expression.Assign(Expression.Property(dict, indexer, key), value); if (arg.Settings.IgnoreNullValues == true && (!getter.Type.GetTypeInfo().IsValueType || getter.Type.IsNullable())) { var condition = Expression.NotEqual(getter, Expression.Constant(null, getter.Type)); itemSet = Expression.IfThen(condition, itemSet); } lines.Add(itemSet); } return Expression.Block(new[] {dict}, lines); }
protected virtual Expression CreateExpressionBody(Expression source, Expression destination, CompileArgument arg) { if (this.CheckExplicitMapping && arg.Context.Config.RequireExplicitMapping && !arg.Context.Config.RuleMap.ContainsKey(new TypeTuple(arg.SourceType, arg.DestinationType))) { throw new InvalidOperationException( $"Implicit mapping is not allowed (check GlobalSettings.RequireExplicitMapping) and no configuration exists for the following mapping: TSource: {arg.SourceType} TDestination: {arg.DestinationType}"); } return CanInline(source, destination, arg) ? CreateInlineExpressionBody(source, arg).To(arg.DestinationType, true) : CreateBlockExpressionBody(source, destination, arg); }
protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) { var newInstance = (NewExpression)CreateInstantiationExpression(source, arg); var properties = CreateAdapterModel(source, newInstance, arg); var lines = new List<MemberBinding>(); foreach (var property in properties) { var getter = CreateAdaptExpression(property.Getter, property.Setter.Type, arg); var bind = Expression.Bind(property.SetterProperty, getter); lines.Add(bind); } return Expression.MemberInit(newInstance, lines); }
protected Expression CreateBlockExpressionBody(Expression source, Expression destination, CompileArgument arg) { var result = Expression.Variable(arg.DestinationType); Expression assign = Expression.Assign(result, destination ?? CreateInstantiationExpression(source, arg)); var set = CreateBlockExpression(source, result, arg); if (arg.Settings.PreserveReference == true && arg.MapType != MapType.Projection && !arg.SourceType.GetTypeInfo().IsValueType && !arg.DestinationType.GetTypeInfo().IsValueType) { var dict = Expression.Parameter(typeof (Dictionary<object, object>)); var propInfo = typeof(MapContext).GetProperty("Context", BindingFlags.Static | BindingFlags.Public); var refContext = Expression.Property(null, propInfo); var refDict = Expression.Property(refContext, "References"); var refAdd = Expression.Call(dict, "Add", null, Expression.Convert(source, typeof(object)), Expression.Convert(result, typeof(object))); set = Expression.Block(assign, refAdd, set); var cached = Expression.Variable(typeof(object)); var tryGetMethod = typeof(Dictionary<object, object>).GetMethod("TryGetValue", new[] { typeof(object), typeof(object).MakeByRefType() }); var checkHasRef = Expression.Call(dict, tryGetMethod, source, cached); var assignDict = Expression.Assign(dict, refDict); set = Expression.IfThenElse( checkHasRef, ExpressionEx.Assign(result, cached), set); set = Expression.Block(new[] { cached, dict }, assignDict, set); } else { set = Expression.Block(assign, set); } if (arg.MapType != MapType.Projection && (!arg.SourceType.GetTypeInfo().IsValueType || arg.SourceType.IsNullable())) { var compareNull = Expression.Equal(source, Expression.Constant(null, source.Type)); set = Expression.IfThenElse( compareNull, Expression.Assign(result, destination ?? Expression.Constant(arg.DestinationType.GetDefault(), arg.DestinationType)), set); } return Expression.Block(new[] { result }, set, result); }
protected Expression CreateAdaptExpression(Expression source, Type destinationType, CompileArgument arg) { if (source.Type == destinationType && (arg.Settings.ShallowCopyForSameType == true || arg.MapType == MapType.Projection)) return source.To(destinationType); var lambda = arg.Context.Config.CreateInlineMapExpression(source.Type, destinationType, arg.MapType, arg.Context); var exp = lambda.Apply(source); if (arg.Settings.DestinationTransforms.Transforms.ContainsKey(exp.Type)) { var transform = arg.Settings.DestinationTransforms.Transforms[exp.Type]; var replacer = new ParameterExpressionReplacer(transform.Parameters, exp); var newExp = replacer.Visit(transform.Body); exp = replacer.ReplaceCount >= 2 ? Expression.Invoke(transform, exp) : newExp; } return exp.To(destinationType); }
protected override Expression CreateInstantiationExpression(Expression source, CompileArgument arg) { var destinationElementType = arg.DestinationType.ExtractCollectionType(); if (arg.DestinationType.IsArray) return Expression.NewArrayBounds(destinationElementType, CreateCountExpression(source, true)); var count = CreateCountExpression(source, false); var listType = arg.DestinationType.GetTypeInfo().IsInterface ? typeof (List<>).MakeGenericType(destinationElementType) : arg.DestinationType; if (count == null) return Expression.New(listType); var ctor = listType.GetConstructor(new[] { typeof(int) }); if (ctor == null) return Expression.New(listType); else return Expression.New(ctor, count); }
protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { if (destination.Type.IsArray) { return CreateArraySet(source, destination, arg); } else { var destinationElementType = destination.Type.ExtractCollectionType(); var listType = destination.Type.IsGenericEnumerableType() || destination.Type.GetInterfaces().Any(ReflectionUtils.IsGenericEnumerableType) ? typeof(ICollection<>).MakeGenericType(destinationElementType) : typeof(IList); var tmp = Expression.Variable(listType); var assign = ExpressionEx.Assign(tmp, destination); //convert to list type var set = CreateListSet(source, tmp, arg); return Expression.Block(new[] {tmp}, assign, set); } }
protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) { if (arg.DestinationType.GetTypeInfo().IsAssignableFrom(source.Type.GetTypeInfo()) && (arg.Settings.ShallowCopyForSameType == true || arg.MapType == MapType.Projection)) return source.To(arg.DestinationType); var sourceElementType = source.Type.ExtractCollectionType(); var destinationElementType = arg.DestinationType.ExtractCollectionType(); var method = (from m in typeof (Enumerable).GetMethods() where m.Name == "Select" let p = m.GetParameters()[1] where p.ParameterType.GetGenericTypeDefinition() == typeof (Func<,>) select m).First().MakeGenericMethod(sourceElementType, destinationElementType); var p1 = Expression.Parameter(sourceElementType); var adapt = CreateAdaptExpression(p1, destinationElementType, arg); if (adapt == p1) return source; return Expression.Call(method, source, Expression.Lambda(adapt, p1)); }
protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) { //new TDestination { // Prop1 = convert(src.Prop1), // Prop2 = convert(src.Prop2), //} var exp = CreateInstantiationExpression(source, arg); var memberInit = exp as MemberInitExpression; var newInstance = memberInit?.NewExpression ?? (NewExpression)exp; var classConverter = CreateClassConverter(source, newInstance, arg); var properties = classConverter.Members; var lines = new List<MemberBinding>(); if (memberInit != null) lines.AddRange(memberInit.Bindings); foreach (var property in properties) { var getter = CreateAdaptExpression(property.Getter, property.Setter.Type, arg); //special null property check for projection //if we don't set null to property, EF will create empty object //except collection type & complex type which cannot be null if (arg.MapType == MapType.Projection && property.Getter.Type != property.Setter.Type && !property.Getter.Type.IsCollection() && !property.Setter.Type.IsCollection() && property.Getter.Type.GetTypeInfo().GetCustomAttributes(true).All(attr => attr.GetType().Name != "ComplexTypeAttribute") && (!property.Getter.Type.GetTypeInfo().IsValueType || property.Getter.Type.IsNullable())) { var compareNull = Expression.Equal(property.Getter, Expression.Constant(null, property.Getter.Type)); getter = Expression.Condition( compareNull, Expression.Constant(property.Setter.Type.GetDefault(), property.Setter.Type), getter); } var bind = Expression.Bind((MemberInfo)property.SetterInfo, getter); lines.Add(bind); } return Expression.MemberInit(newInstance, lines); }
protected override bool CanInline(Expression source, Expression destination, CompileArgument arg) { if (!base.CanInline(source, destination, arg)) return false; if (arg.MapType == MapType.Projection) { if (arg.DestinationType.IsAssignableFromList()) return true; throw new InvalidOperationException( $"{arg.DestinationType} is not supported for projection, please consider using List<>: TSource: {arg.SourceType} TDestination: {arg.DestinationType}"); } if (arg.DestinationType == typeof (IEnumerable) || arg.DestinationType.IsGenericEnumerableType()) return true; return false; }
protected virtual bool CanInline(Expression source, Expression destination, CompileArgument arg) { if (arg.MapType == MapType.MapToTarget) return false; if (arg.Settings.ConstructUsing != null && arg.Settings.ConstructUsing.Body.NodeType != ExpressionType.New && arg.Settings.ConstructUsing.Body.NodeType != ExpressionType.MemberInit) { if (arg.MapType == MapType.Projection) throw new InvalidOperationException( $"Input ConstructUsing is invalid for projection: TSource: {arg.SourceType} TDestination: {arg.DestinationType}"); return false; } if (arg.Settings.PreserveReference == true && arg.MapType != MapType.Projection && !arg.SourceType.GetTypeInfo().IsValueType && !arg.DestinationType.GetTypeInfo().IsValueType) return false; return true; }
protected override Expression CreateInstantiationExpression(Expression source, CompileArgument arg) { var destinationElementType = arg.DestinationType.ExtractCollectionType(); if (arg.DestinationType.IsArray) return Expression.NewArrayBounds( destinationElementType, CreateCountExpression(source, true)); //new TDestinationElement[count] var count = CreateCountExpression(source, false); var listType = arg.DestinationType.GetTypeInfo().IsInterface ? typeof (List<>).MakeGenericType(destinationElementType) : arg.DestinationType; if (count == null) return Expression.New(listType); //new List<T>() var ctor = (from c in listType.GetConstructors() let args = c.GetParameters() where args.Length == 1 && args[0].ParameterType == typeof (int) select c).FirstOrDefault(); if (ctor == null) return Expression.New(listType); //new List<T>() else return Expression.New(ctor, count); //new List<T>(count) }
protected override Expression CreateInstantiationExpression(Expression source, CompileArgument arg) { //new TDestination(src.Prop1, src.Prop2) if (arg.Settings.ConstructUsingFactory != null) return base.CreateInstantiationExpression(source, arg); var classConverter = CreateClassConverter(source, null, arg); var properties = classConverter.Members; var arguments = new List<Expression>(); foreach (var property in properties) { var parameterInfo = (ParameterInfo) property.SetterInfo; var defaultValue = parameterInfo.IsOptional ? parameterInfo.DefaultValue : parameterInfo.ParameterType.GetDefault(); Expression getter; if (property.Getter == null) { getter = Expression.Constant(defaultValue, property.Setter.Type); } else { getter = CreateAdaptExpression(property.Getter, property.Setter.Type, arg); if (arg.Settings.IgnoreNullValues == true && (!property.Getter.Type.GetTypeInfo().IsValueType || property.Getter.Type.IsNullable())) { var condition = Expression.NotEqual(property.Getter, Expression.Constant(null, property.Getter.Type)); getter = Expression.Condition(condition, getter, Expression.Constant(defaultValue, property.Setter.Type)); } } arguments.Add(getter); } return Expression.New(classConverter.ConstructorInfo, arguments); }
protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) { //new TDestination { // { "Prop1", convert(src.Prop1) }, // { "Prop2", convert(src.Prop2) }, //} var exp = CreateInstantiationExpression(source, arg); var listInit = exp as ListInitExpression; var newInstance = listInit?.NewExpression ?? (NewExpression)exp; var dictType = arg.DestinationType.GetDictionaryType(); var dictTypeArgs = dictType.GetGenericArguments(); var keyType = dictTypeArgs[0]; var valueType = dictTypeArgs[1]; var add = dictType.GetMethod("Add", new[] { keyType, valueType }); var lines = new List<ElementInit>(); if (listInit != null) lines.AddRange(listInit.Initializers); var nameMatching = arg.Settings.NameMatchingStrategy; var properties = source.Type.GetPublicFieldsAndProperties(); foreach (var property in properties) { var getter = property.GetExpression(source); var value = CreateAdaptExpression(getter, valueType, arg); Expression key = Expression.Constant(nameMatching.SourceMemberNameConverter(property.Name)); key = CreateAdaptExpression(key, keyType, arg); var itemInit = Expression.ElementInit(add, key, value); lines.Add(itemInit); } return Expression.ListInit(newInstance, lines); }
private static Expression InvokeMapping(Func <CompileArgument, LambdaExpression> mappingFactory, Expression source, Expression result, CompileArgument arg, bool setResult) { var afterMapping = mappingFactory(arg); var args = afterMapping.Parameters; var invoke = afterMapping.Apply(arg.MapType, source.To(args[0].Type), result.To(args[1].Type)); if (invoke.Type != typeof(void) && setResult) { invoke = ExpressionEx.Assign(result, invoke); } return(invoke); }
protected override Expression CreateInstantiationExpression(Expression source, Expression?destination, CompileArgument arg) { return(Expression.NewArrayBounds(arg.DestinationType.GetElementType(), GetArrayBounds(source, arg.DestinationType))); }
protected virtual Expression CreateExpressionBody(Expression source, Expression?destination, CompileArgument arg) { if (this.CheckExplicitMapping && arg.Context.Config.RequireExplicitMapping && !arg.ExplicitMapping) { throw new InvalidOperationException("Implicit mapping is not allowed (check GlobalSettings.RequireExplicitMapping) and no configuration exists"); } var oldMaxDepth = arg.Context.MaxDepth; var oldDepth = arg.Context.Depth; try { if (!arg.Context.MaxDepth.HasValue && arg.Settings.MaxDepth.HasValue) { arg.Context.MaxDepth = arg.Settings.MaxDepth; arg.Context.Depth = 0; } if (arg.Context.MaxDepth.HasValue) { if (this.ObjectType != ObjectType.Primitive && arg.Context.Depth >= arg.Context.MaxDepth.Value) { return(arg.DestinationType.CreateDefault()); } if (this.ObjectType == ObjectType.Class) { arg.Context.Depth++; } } if (CanInline(source, destination, arg)) { var exp = CreateInlineExpressionBody(source, arg); if (exp != null) { return(exp.To(arg.DestinationType, true)); } } if (arg.Context.Running.Count > 1 && !arg.Context.Config.SelfContainedCodeGeneration && !arg.UseDestinationValue && !arg.Context.IsSubFunction()) { if (destination == null) { return(arg.Context.Config.CreateMapInvokeExpressionBody(source.Type, arg.DestinationType, source)); } else { return(arg.Context.Config.CreateMapToTargetInvokeExpressionBody(source.Type, arg.DestinationType, source, destination)); } } else { return(CreateBlockExpressionBody(source, destination, arg)); } } finally { arg.Context.Depth = oldDepth; arg.Context.MaxDepth = oldMaxDepth; } }
protected abstract Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg);
private static Expression CreateAdaptExpressionCore(Expression source, Type destinationType, CompileArgument arg, MemberMapping?mapping = null, Expression?destination = null) { var mapType = arg.MapType == MapType.MapToTarget && destination == null ? MapType.Map : mapping?.UseDestinationValue == true ? MapType.MapToTarget : arg.MapType; var extraParams = new HashSet <ParameterExpression>(); try { if (mapping != null && mapping.HasSettings()) { if (arg.Context.ExtraParameters.Add(mapping.Source)) { extraParams.Add(mapping.Source); } if (mapping.Destination != null && arg.Context.ExtraParameters.Add(mapping.Destination)) { extraParams.Add(mapping.Destination); } } var lambda = arg.Context.Config.CreateInlineMapExpression(source.Type, destinationType, mapType, arg.Context, mapping); var paramList = new List <Expression> { source }; if (destination != null) { paramList.Add(destination); } paramList.AddRange(lambda.Parameters.Skip(paramList.Count)); if (!lambda.IsMultiLine()) { return(lambda.Apply(arg.MapType, paramList.ToArray())); } return(Expression.Invoke(lambda, paramList.ToArray())); } finally { arg.Context.ExtraParameters.ExceptWith(extraParams); } }
protected virtual Expression CreateInstantiationExpression(Expression source, CompileArgument arg) { //new TDestination() return arg.Settings.ConstructUsing != null ? arg.Settings.ConstructUsing.Apply(source).TrimConversion().To(arg.DestinationType) : Expression.New(arg.DestinationType); }
protected Expression CreateBlockExpressionBody(Expression source, Expression?destination, CompileArgument arg) { if (arg.MapType == MapType.Projection) { throw new InvalidOperationException("Mapping is invalid for projection"); } var vars = new List <ParameterExpression>(); var blocks = new List <Expression>(); var label = Expression.Label(arg.DestinationType); //var drvdSource = source as TDerivedSource //if (drvdSource != null) // return adapt<TSource, TDest>(drvdSource); foreach (var tuple in arg.Settings.Includes) { //same type, no redirect to prevent endless loop if (tuple.Source == arg.SourceType) { continue; } //type is not compatible, no redirect if (!arg.SourceType.GetTypeInfo().IsAssignableFrom(tuple.Source.GetTypeInfo())) { continue; } var drvdSource = Expression.Variable(tuple.Source); vars.Add(drvdSource); var drvdSourceAssign = Expression.Assign( drvdSource, Expression.TypeAs(source, tuple.Source)); blocks.Add(drvdSourceAssign); var cond = Expression.NotEqual(drvdSource, Expression.Constant(null, tuple.Source)); ParameterExpression?drvdDest = null; if (destination != null) { drvdDest = Expression.Variable(tuple.Destination); vars.Add(drvdDest); var drvdDestAssign = Expression.Assign( drvdDest, Expression.TypeAs(destination, tuple.Destination)); blocks.Add(drvdDestAssign); cond = Expression.AndAlso( cond, Expression.NotEqual(drvdDest, Expression.Constant(null, tuple.Destination))); } var adaptExpr = CreateAdaptExpressionCore(drvdSource, tuple.Destination, arg, destination: drvdDest); var adapt = Expression.Return(label, adaptExpr); var ifExpr = Expression.IfThen(cond, adapt); blocks.Add(ifExpr); } //new TDest(); Expression transformedSource = source; var transform = TransformSource(source); if (transform != source) { var src = Expression.Variable(transform.Type); vars.Add(src); transformedSource = src; } var set = CreateInstantiationExpression(transformedSource, destination, arg); if (destination != null && (this.UseTargetValue || arg.UseDestinationValue) && arg.GetConstructUsing()?.Parameters.Count != 2 && destination.CanBeNull()) { //dest ?? new TDest(); set = Expression.Coalesce(destination, set); } if (set.NodeType == ExpressionType.Throw) { blocks.Add(set); } else { //TDestination result; //if (source == null) // return default(TDestination); if (source.CanBeNull()) { var compareNull = Expression.Equal(source, Expression.Constant(null, source.Type)); blocks.Add( Expression.IfThen(compareNull, Expression.Return(label, arg.DestinationType.CreateDefault())) ); } //var result = new TDest(); var result = Expression.Variable(arg.DestinationType, "result"); var assign = Expression.Assign(result, set); var assignActions = new List <Expression>(); if (transform != source) { assignActions.Add(Expression.Assign(transformedSource, transform)); } assignActions.Add(assign); //before(source, result); var beforeMappings = arg.Settings.BeforeMappingFactories.Select(it => InvokeMapping(it, source, result, arg, true)); assignActions.AddRange(beforeMappings); //result.prop = adapt(source.prop); var mapping = CreateBlockExpression(transformedSource, result, arg); var settingActions = new List <Expression> { mapping }; //after(source, result); var afterMappings = arg.Settings.AfterMappingFactories.Select(it => InvokeMapping(it, source, result, arg, false)); settingActions.AddRange(afterMappings); //return result; settingActions.Add(Expression.Return(label, result)); //using (var scope = new MapContextScope()) { // var references = scope.Context.Reference; // var key = new ReferenceTuple(source, typeof(TDestination)); // if (references.TryGetValue(key, out var cache)) // return (TDestination)cache; // // var result = new TDestination(); // references[source] = (object)result; // result.prop = adapt(source.prop); // return result; //} if (arg.Settings.PreserveReference == true && arg.SourceType.IsObjectReference() && arg.DestinationType.IsObjectReference()) { var scope = Expression.Variable(typeof(MapContextScope), "scope"); vars.Add(scope); var newScope = Expression.Assign(scope, Expression.New(typeof(MapContextScope))); blocks.Add(newScope); var dictType = typeof(Dictionary <ReferenceTuple, object>); var references = Expression.Variable(dictType, "references"); var refContext = Expression.Property(scope, "Context"); var refDict = Expression.Property(refContext, "References"); var assignReferences = Expression.Assign(references, refDict); var tupleType = typeof(ReferenceTuple); var key = Expression.Variable(tupleType, "key"); var assignKey = Expression.Assign(key, Expression.New(tupleType.GetConstructor(new[] { typeof(object), typeof(Type) }) !, source, Expression.Constant(arg.DestinationType))); var cache = Expression.Variable(typeof(object), "cache"); var tryGetMethod = dictType.GetMethod("TryGetValue", new[] { typeof(ReferenceTuple), typeof(object).MakeByRefType() }); var checkHasRef = Expression.Call(references, tryGetMethod !, key, cache); var setResult = Expression.IfThen( checkHasRef, Expression.Return(label, cache.To(arg.DestinationType))); var indexer = dictType.GetProperties().First(item => item.GetIndexParameters().Length > 0); var refAssign = Expression.Assign( Expression.Property(references, indexer, key), Expression.Convert(result, typeof(object))); assignActions.Add(refAssign); var usingBody = Expression.Block( new[] { cache, references, key, result }, new Expression[] { assignReferences, assignKey, setResult } .Concat(assignActions) .Concat(settingActions)); var dispose = Expression.Call(scope, "Dispose", null); blocks.Add(Expression.TryFinally(usingBody, dispose)); } else { vars.Add(result); blocks.AddRange(assignActions); blocks.AddRange(settingActions); } } blocks.Add(Expression.Label(label, arg.DestinationType.CreateDefault())); return(Expression.Block(vars, blocks)); }
protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) { return CreateInstantiationExpression(source, arg); }
protected abstract Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg);
protected override Expression CreateInstantiationExpression(Expression source, Expression destination, CompileArgument arg) { var destinationElementType = arg.DestinationType.ExtractCollectionType(); var count = ExpressionEx.CreateCountExpression(source, false); var listType = arg.DestinationType.GetTypeInfo().IsInterface ? typeof(List <>).MakeGenericType(destinationElementType) : arg.DestinationType; if (count == null) { return(Expression.New(listType)); //new List<T>() } var ctor = (from c in listType.GetConstructors() let args = c.GetParameters() where args.Length == 1 && args[0].ParameterType == typeof(int) select c).FirstOrDefault(); if (ctor == null) { return(Expression.New(listType)); //new List<T>() } else { return(Expression.New(ctor, count)); //new List<T>(count) } }
private Expression CreateArraySet(Expression source, Expression destination, CompileArgument arg) { //var v0 = 0, v1 = 0; //var vlen0 = dest.GetLength(0), vlen1 = dest.GetLength(1); //for (var i = 0, len = src.Count; i < len; i++) { // var item = src[i]; // dest[v0, v1] = convert(item); // v1++; // if (v1 >= vlen1) { // v1 = 0; // v0++; // } //} var sourceElementType = source.Type.ExtractCollectionType(); var destinationElementType = destination.Type.ExtractCollectionType(); var item = Expression.Variable(sourceElementType, "item"); var vx = Enumerable.Range(0, destination.Type.GetArrayRank()) .Select(i => Expression.Variable(typeof(int), "v" + i)) .ToList(); var vlenx = Enumerable.Range(0, destination.Type.GetArrayRank()) .Select(i => Expression.Variable(typeof(int), "vlen" + i)) .ToList(); var block = new List <Expression>(); block.AddRange(vx.Select(v => Expression.Assign(v, Expression.Constant(0)))); var method = typeof(Array).GetMethod("GetLength", new[] { typeof(int) }); block.AddRange( vlenx.Select((vlen, i) => Expression.Assign( vlen, Expression.Call(destination, method, Expression.Constant(i))))); var getter = CreateAdaptExpression(item, destinationElementType, arg); var set = ExpressionEx.Assign( Expression.ArrayAccess(destination, vx), getter); Expression ifExpr = Expression.Block( Expression.Assign(vx[1], Expression.Constant(0)), Expression.PostIncrementAssign(vx[0])); for (var i = 1; i < vx.Count; i++) { var list = new List <Expression>(); if (i + 1 < vx.Count) { list.Add(Expression.Assign(vx[i + 1], Expression.Constant(0))); } list.Add(Expression.PostIncrementAssign(vx[i])); list.Add(Expression.IfThen( Expression.GreaterThanOrEqual(vx[i], vlenx[i]), ifExpr)); ifExpr = Expression.Block(list); } var loop = ExpressionEx.ForLoop(source, item, set, ifExpr); block.Add(loop); return(Expression.Block(vx.Concat(vlenx), block)); }
protected Expression CreateAdaptExpression(Expression source, Type destinationType, CompileArgument arg, Expression?destination = null) { return(CreateAdaptExpression(source, destinationType, arg, null, destination)); }
protected abstract Expression?CreateInlineExpression(Expression source, CompileArgument arg);
protected override bool CanInline(Expression source, Expression?destination, CompileArgument arg) { return(false); }
protected override Expression CreateInstantiationExpression(Expression source, Expression?destination, CompileArgument arg) { if (destination == null) { return(_lambda.Apply(arg.MapType, source)); } else { return(_lambda.Apply(arg.MapType, source, destination)); } }
protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { return(Expression.Empty()); }
protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { //### !IgnoreNullValues //dest.Prop1 = convert(src.Prop1); //dest.Prop2 = convert(src.Prop2); //### IgnoreNullValues //if (src.Prop1 != null) // dest.Prop1 = convert(src.Prop1); //if (src.Prop2 != null) // dest.Prop2 = convert(src.Prop2); var classConverter = CreateClassConverter(source, destination, arg); var properties = classConverter.Members; var lines = new List <Expression>(); foreach (var property in properties) { var getter = CreateAdaptExpression(property.Getter, property.Setter.Type, arg); Expression itemAssign = Expression.Assign(property.Setter, getter); if (arg.Settings.IgnoreNullValues == true && (!property.Getter.Type.GetTypeInfo().IsValueType || property.Getter.Type.IsNullable())) { var condition = Expression.NotEqual(property.Getter, Expression.Constant(null, property.Getter.Type)); itemAssign = Expression.IfThen(condition, itemAssign); } lines.Add(itemAssign); } return(Expression.Block(lines)); }
protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) { throw new NotImplementedException(); }
protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { var destinationElementType = destination.Type.ExtractCollectionType(); var listType = destination.Type.GetGenericEnumerableType() != null ? typeof(ICollection <>).MakeGenericType(destinationElementType) : typeof(IList); var tmp = Expression.Variable(listType, "list"); var assign = ExpressionEx.Assign(tmp, destination); //convert to list type var set = CreateListSet(source, tmp, arg); return(Expression.Block(new[] { tmp }, assign, set)); }
protected virtual Expression CreateInstantiationExpression(Expression source, Expression?destination, CompileArgument arg) { //new TDestination() //if there is constructUsing, use constructUsing var constructUsing = arg.GetConstructUsing(); if (constructUsing != null) { var args = destination == null ? new[] { source } : new[] { source, destination }; return(constructUsing.Apply(arg.MapType, args) .TrimConversion(true) .To(arg.DestinationType)); } //if there is default constructor, use default constructor else if (arg.DestinationType.HasDefaultConstructor()) { return(Expression.New(arg.DestinationType)); } //if mapToTarget or include derived types, allow mapping & throw exception on runtime //instantiation is not needed else if (destination != null || arg.Settings.Includes.Count > 0) { return(Expression.Throw( Expression.New( // ReSharper disable once AssignNullToNotNullAttribute typeof(InvalidOperationException).GetConstructor(new[] { typeof(string) }), Expression.Constant("Cannot instantiate type: " + arg.DestinationType.Name)), arg.DestinationType)); } //if mapping to interface, create dynamic type implementing it else if (arg.DestinationType.GetTypeInfo().IsInterface) { return(Expression.New(DynamicTypeGenerator.GetTypeForInterface(arg.DestinationType))); } //otherwise throw else { throw new InvalidOperationException($"No default constructor for type '{arg.DestinationType.Name}', please use 'ConstructUsing' or 'MapWith'"); } }
protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) { return(CreateInstantiationExpression(source, arg)); }
protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { if (source.Type.IsArray && source.Type.GetArrayRank() == destination.Type.GetArrayRank() && source.Type.GetElementType() == destination.Type.GetElementType() && source.Type.GetElementType().IsPrimitiveKind()) { //Array.Copy(src, 0, dest, 0, src.Length) var method = typeof(Array).GetMethod("Copy", new[] { typeof(Array), typeof(int), typeof(Array), typeof(int), typeof(int) }); return(Expression.Call(method, source, Expression.Constant(0), destination, Expression.Constant(0), ExpressionEx.CreateCountExpression(source, true))); } else { return(CreateArraySet(source, destination, arg)); } }
protected Expression CreateBlockExpressionBody(Expression source, Expression destination, CompileArgument arg) { var result = Expression.Variable(arg.DestinationType); Expression assign = Expression.Assign(result, destination ?? CreateInstantiationExpression(source, arg)); var set = CreateBlockExpression(source, result, arg); if (arg.Settings.PreserveReference == true && arg.MapType != MapType.Projection && !arg.SourceType.GetTypeInfo().IsValueType && !arg.DestinationType.GetTypeInfo().IsValueType) { //using (var scope = new MapContextScope()) { // var dict = scope.Context.Reference; // object cache; // if (dict.TryGetValue(source, out cache)) // result = (TDestination)cache; // else { // result = new TDestination(); // dict.Add(source, (object)result); // result.Prop1 = adapt(source.Prop1); // result.Prop2 = adapt(source.Prop2); // } //} var scope = Expression.Variable(typeof(MapContextScope)); var newScope = Expression.Assign(scope, Expression.New(typeof(MapContextScope))); var dict = Expression.Variable(typeof (Dictionary<object, object>)); var refContext = Expression.Property(scope, "Context"); var refDict = Expression.Property(refContext, "References"); var assignDict = Expression.Assign(dict, refDict); var refAdd = Expression.Call(dict, "Add", null, Expression.Convert(source, typeof(object)), Expression.Convert(result, typeof(object))); var setResultAndCache = Expression.Block(assign, refAdd, set); var cached = Expression.Variable(typeof(object)); var tryGetMethod = typeof(Dictionary<object, object>).GetMethod("TryGetValue", new[] { typeof(object), typeof(object).MakeByRefType() }); var checkHasRef = Expression.Call(dict, tryGetMethod, source, cached); var setResult = Expression.IfThenElse( checkHasRef, ExpressionEx.Assign(result, cached), setResultAndCache); var usingBody = Expression.Block(new[] { cached, dict }, assignDict, setResult); var dispose = Expression.Call(scope, "Dispose", null); set = Expression.Block(new[] { scope }, newScope, Expression.TryFinally(usingBody, dispose)); } else { set = Expression.Block(assign, set); } //TDestination result; //if (source == null) // result = default(TDestination); //else // result = adapt(source); //return result; if (arg.MapType != MapType.Projection && (!arg.SourceType.GetTypeInfo().IsValueType || arg.SourceType.IsNullable())) { var compareNull = Expression.Equal(source, Expression.Constant(null, source.Type)); set = Expression.IfThenElse( compareNull, Expression.Assign(result, destination ?? Expression.Constant(arg.DestinationType.GetDefault(), arg.DestinationType)), set); } return Expression.Block(new[] { result }, set, result); }
internal Expression CreateAdaptExpression(Expression source, Type destinationType, CompileArgument arg, MemberMapping?mapping, Expression?destination = null) { if (source.Type == destinationType && (arg.Settings.ShallowCopyForSameType == true || arg.MapType == MapType.Projection)) { return(source); } //adapt(source); var exp = CreateAdaptExpressionCore(source, destinationType, arg, mapping, destination); //transform(adapt(source)); var transform = arg.Settings.DestinationTransforms.Find(it => it.Condition(exp.Type)); if (transform != null) { exp = transform.TransformFunc(exp.Type).Apply(arg.MapType, exp); } return(exp.To(destinationType)); }
protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { return Expression.Empty(); }
protected Expression CreateInstantiationExpression(Expression source, CompileArgument arg) { return(CreateInstantiationExpression(source, null, arg)); }
protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { throw new NotImplementedException(); }
protected override Expression CreateInstantiationExpression(Expression source, CompileArgument arg) { //new TDestination(src.Prop1, src.Prop2) if (arg.Settings.ConstructUsingFactory != null) { return(base.CreateInstantiationExpression(source, arg)); } var classConverter = CreateClassConverter(source, null, arg); var properties = classConverter.Members; var arguments = new List <Expression>(); foreach (var property in properties) { var parameterInfo = (ParameterInfo)property.SetterInfo; var defaultValue = parameterInfo.IsOptional ? parameterInfo.DefaultValue : parameterInfo.ParameterType.GetDefault(); Expression getter; if (property.Getter == null) { getter = Expression.Constant(defaultValue, property.Setter.Type); } else { getter = CreateAdaptExpression(property.Getter, property.Setter.Type, arg); if (arg.Settings.IgnoreNullValues == true && (!property.Getter.Type.GetTypeInfo().IsValueType || property.Getter.Type.IsNullable())) { var condition = Expression.NotEqual(property.Getter, Expression.Constant(null, property.Getter.Type)); getter = Expression.Condition(condition, getter, Expression.Constant(defaultValue, property.Setter.Type)); } } arguments.Add(getter); } return(Expression.New(classConverter.ConstructorInfo, arguments)); }
protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) { throw new NotImplementedException(); }
protected ClassMapping CreateClassConverter(Expression source, Expression destination, CompileArgument arg) { var classModel = GetClassModel(arg.DestinationType); var destinationMembers = classModel.Members; var unmappedDestinationMembers = new List <string>(); var properties = new List <MemberMapping>(); foreach (var destinationMember in destinationMembers) { if (ProcessIgnores(arg.Settings, destinationMember)) { continue; } var member = destinationMember; var getter = arg.Settings.ValueAccessingStrategies .Select(fn => fn(source, member, arg)) .FirstOrDefault(result => result != null); if (getter != null) { var propertyModel = new MemberMapping { Getter = getter, Setter = destinationMember.GetExpression(destination), SetterInfo = destinationMember.Info, }; properties.Add(propertyModel); } else if (classModel.ConstructorInfo != null) { var propertyModel = new MemberMapping { Getter = null, Setter = destinationMember.GetExpression(destination), SetterInfo = destinationMember.Info, }; properties.Add(propertyModel); } else if (destinationMember.SetterModifier != AccessModifier.None) { unmappedDestinationMembers.Add(destinationMember.Name); } } if (arg.Context.Config.RequireDestinationMemberSource && unmappedDestinationMembers.Count > 0) { throw new ArgumentOutOfRangeException($"The following members of destination class {arg.DestinationType} do not have a corresponding source member mapped or ignored:{string.Join(",", unmappedDestinationMembers)}"); } return(new ClassMapping { ConstructorInfo = classModel.ConstructorInfo, Members = properties, }); }