public virtual Expression GetMemberAssignment(MemberMappingContext context, out bool needsTrackingOrRecursion) { Expression newInstance = this.GetMemberNewInstance(context, out bool isMapComplete); needsTrackingOrRecursion = !isMapComplete; if (isMapComplete) { return(newInstance); } bool isCreateNewInstance = context.Options.ReferenceBehavior == ReferenceBehaviors.CREATE_NEW_INSTANCE; if (isCreateNewInstance || context.TargetMemberValueGetter == null) { return(Expression.Assign(context.TargetMember, newInstance)); } //ReferenceBehaviors.USE_TARGET_INSTANCE_IF_NOT_NULL return(Expression.Block ( Expression.Assign(context.TargetMember, context.TargetMemberValueGetter), Expression.IfThen ( Expression.Equal(context.TargetMember, context.TargetMemberNullValue), Expression.Assign(context.TargetMember, newInstance) ) )); }
private Expression GetSimpleMemberExpression(MemberMapping mapping) { var memberContext = new MemberMappingContext(mapping); ParameterExpression value = Expression.Parameter(mapping.TargetMember.MemberType, "returnValue"); var targetSetterInstanceParamName = mapping.TargetMember.ValueSetter.Parameters[0].Name; var targetSetterMemberParamName = mapping.TargetMember.ValueSetter.Parameters[1].Name; return(Expression.Block ( new[] { memberContext.SourceMember, value }, Expression.Assign(memberContext.SourceMember, memberContext.SourceMemberValueGetter), Expression.Assign(value, mapping.MappingExpression.Body .ReplaceParameter(memberContext.SourceMember, mapping.MappingExpression.Parameters[0].Name)), mapping.TargetMember.ValueSetter.Body .ReplaceParameter(memberContext.TargetInstance, targetSetterInstanceParamName) .ReplaceParameter(value, targetSetterMemberParamName), Expression.Invoke(debugExp, Expression.Convert(memberContext.TargetInstance, typeof(object))) )); }
/// <summary> /// Returns an expression calling Expression.New. /// Expression.New will call a constructor intializing the capacity of the collection /// </summary> protected virtual Expression GetNewInstanceWithReservedCapacity(MemberMappingContext context) { var constructorWithCapacity = context.TargetMember.Type.GetConstructor(new Type[] { typeof(int) }); if (constructorWithCapacity == null) { return(null); } //It is forbidden to use nameof with unbound generic types. We use 'int' just to get around that. var getCountProperty = context.SourceMember.Type.GetProperty(nameof(ICollection <int> .Count), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); if (getCountProperty == null) { //ICollection<T> interface implementation is injected in the Array class at runtime. //Array implements ICollection.Count explicitly. For simplicity, we just look for property Length :) getCountProperty = context.SourceMember.Type.GetProperty(nameof(Array.Length)); } var getCountMethod = getCountProperty.GetGetMethod(); return(Expression.New(constructorWithCapacity, Expression.Call(context.SourceMember, getCountMethod))); }
protected override Expression GetNewInstanceFromSourceCollection(MemberMappingContext context, CollectionMapperContext collectionContext) { var targetConstructor = context.TargetMember.Type.GetConstructor( new[] { typeof(IEnumerable <>).MakeGenericType(collectionContext.TargetCollectionElementType) }); return(Expression.New(targetConstructor, Expression.New(targetConstructor, context.SourceMember))); }
public override Expression GetMemberAssignment(MemberMappingContext context, out bool needsTrackingOrRecursion) { needsTrackingOrRecursion = true; if (context.Options.ReferenceBehavior == ReferenceBehaviors.CREATE_NEW_INSTANCE) { return(base.GetMemberAssignment(context, out needsTrackingOrRecursion)); } //if( context.Options.ReferenceBehavior == ReferenceBehaviors.USE_TARGET_INSTANCE_IF_NOT_NULL ) Expression newInstance = this.GetMemberNewInstance(context, out bool isMapComplete); //FOR ARRAYS WE ALSO CHECK IF THE TARGET ARRAY IS LARGE ENOUGH //TO HOLD ALL OF THE ITEMS OF THE SOURCE COLLECTION. //IF THE ARRAY IS NOT LARGE ENOUGH, WE CREATE A NEW INSTANCE LARGE ENOUGH. var sourceCountMethod = GetCountMethod(context.SourceMember.Type); var targetCountMethod = GetCountMethod(context.TargetMember.Type); Expression sourceCountMethodCallExp; if (sourceCountMethod.IsStatic) { sourceCountMethodCallExp = Expression.Call(null, sourceCountMethod, context.SourceMember); } else { sourceCountMethodCallExp = Expression.Call(context.SourceMember, sourceCountMethod); } Expression targetCountMethodCallExp; if (targetCountMethod.IsStatic) { targetCountMethodCallExp = Expression.Call(null, targetCountMethod, context.TargetMember); } else { targetCountMethodCallExp = Expression.Call(context.TargetMember, targetCountMethod); } return(Expression.Block ( base.GetMemberAssignment(context, out needsTrackingOrRecursion), Expression.IfThen ( Expression.LessThan(targetCountMethodCallExp, sourceCountMethodCallExp), Expression.Assign(context.TargetMember, newInstance) ) )); }
private LambdaExpression GetSimpleMemberExpression(MemberMapping mapping) { var memberContext = new MemberMappingContext(mapping); var targetSetterInstanceParamName = mapping.TargetMember.ValueSetter.Parameters[0].Name; var targetSetterValueParamName = mapping.TargetMember.ValueSetter.Parameters[1].Name; var valueReaderExp = mapping.MappingExpression.Body.ReplaceParameter( memberContext.SourceMemberValueGetter, mapping.MappingExpression.Parameters[0].Name); if (mapping.MappingExpression.Parameters[0].Type == typeof(ReferenceTracker)) { valueReaderExp = mapping.MappingExpression.Body.ReplaceParameter( memberContext.SourceMemberValueGetter, mapping.MappingExpression.Parameters[1].Name); } var expression = mapping.TargetMember.ValueSetter.Body .ReplaceParameter(memberContext.TargetInstance, targetSetterInstanceParamName) .ReplaceParameter(valueReaderExp, targetSetterValueParamName); var exceptionParam = Expression.Parameter(typeof(Exception), "exception"); var ctor = typeof(ArgumentException) .GetConstructor(new Type[] { typeof(string), typeof(Exception) }); var getErrorMsg = Expression.Invoke ( _getErrorExp, Expression.Constant(errorMsg), Expression.Constant(memberContext.Options.ToString()), Expression.Convert(memberContext.SourceMemberValueGetter, typeof(object)), Expression.Constant(memberContext.SourceMember.Type), Expression.Constant(memberContext.TargetMember.Type) ); expression = Expression.TryCatch ( Expression.Block(typeof(void), expression), Expression.Catch(exceptionParam, Expression.Throw ( Expression.New(ctor, getErrorMsg, exceptionParam), typeof(void) )) ); var delegateType = typeof(Action <,>).MakeGenericType( memberContext.SourceInstance.Type, memberContext.TargetInstance.Type); return(Expression.Lambda(delegateType, expression, memberContext.SourceInstance, memberContext.TargetInstance)); }
/// <summary> /// Returns an expression calling Expression.New. /// Expression.New will call a constructor intializing the capacity of the collection /// </summary> protected virtual Expression GetNewInstanceWithReservedCapacity(MemberMappingContext context) { var constructorWithCapacity = context.TargetMember.Type.GetConstructor(new Type[] { typeof(int) }); if (constructorWithCapacity == null) { return(null); } var getCountMethod = this.GetCountMethod(context.SourceMember.Type); return(Expression.New(constructorWithCapacity, Expression.Call(context.SourceMember, getCountMethod))); }
private Expression GetSimpleMemberExpression(MemberMapping mapping) { var memberContext = new MemberMappingContext(mapping); var targetSetterInstanceParamName = mapping.TargetMember.ValueSetter.Parameters[0].Name; var targetSetterValueParamName = mapping.TargetMember.ValueSetter.Parameters[1].Name; var valueReaderExp = mapping.MappingExpression.Body.ReplaceParameter( memberContext.SourceMemberValueGetter, mapping.MappingExpression.Parameters[0].Name); return(mapping.TargetMember.ValueSetter.Body .ReplaceParameter(memberContext.TargetInstance, targetSetterInstanceParamName) .ReplaceParameter(valueReaderExp, targetSetterValueParamName)); }
public override Expression GetTargetInstanceAssignment(MemberMappingContext context, MemberMapping mapping) { if (mapping.ReferenceMappingStrategy == ReferenceMappingStrategies.CREATE_NEW_INSTANCE && context.SourceInstance.Type.ImplementsInterface(typeof(ICollection <>)) && context.TargetInstance.Type.ImplementsInterface(typeof(ICollection <>))) { var constructorWithCapacity = context.TargetInstance.Type.GetConstructor(new Type[] { typeof(int) }); if (constructorWithCapacity != null) { //It is forbidden to use nameof with unbound generic types. We use 'int' just to get around that. var getCountMethod = context.SourceInstance.Type.GetProperty(nameof(ICollection <int> .Count)).GetGetMethod(); return(Expression.Assign(context.TargetInstance, Expression.New(constructorWithCapacity, Expression.Call(context.SourceInstance, getCountMethod)))); } } return(base.GetTargetInstanceAssignment(context, mapping)); }
public virtual Expression GetTargetInstanceAssignment(MemberMappingContext context, MemberMapping mapping) { var newInstanceExp = Expression.New(context.TargetMember.Type); if (mapping.ReferenceMappingStrategy == ReferenceMappingStrategies.CREATE_NEW_INSTANCE) { return(Expression.Assign(context.TargetMember, newInstanceExp)); } return(Expression.Block ( Expression.Assign(context.TargetMember, context.TargetMemberValueGetter), Expression.IfThen ( Expression.Equal(context.TargetMember, context.TargetMemberNullValue), Expression.Assign(context.TargetMember, newInstanceExp) ) )); }
protected virtual Expression GetMemberNewInstance(MemberMappingContext context) { if (context.Options.CustomTargetConstructor != null) { return(Expression.Invoke(context.Options.CustomTargetConstructor)); } //If we are just cloning (ie: mapping on the same type) we prefer to use exactly the //same runtime-type used in the source (in order to manage abstract classes, interfaces and inheritance). if (context.TargetMember.Type.IsAssignableFrom(context.SourceMember.Type)) { MethodInfo getTypeMethodInfo = typeof(object).GetMethod(nameof(object.GetType)); var getSourceType = Expression.Call(context.SourceMemberValueGetter, getTypeMethodInfo); return(Expression.Convert(Expression.Call(null, typeof(InstanceFactory).GetMethods()[1], getSourceType, Expression.Constant(null, typeof(object[]))), context.TargetMember.Type)); } return(Expression.New(context.TargetMember.Type)); }
public virtual Expression GetMemberAssignment(MemberMappingContext context) { Expression newInstance = this.GetMemberNewInstance(context); bool isCreateNewInstance = GetIsCreateNewInstance(context); if (isCreateNewInstance || context.TargetMemberValueGetter == null) { return(Expression.Assign(context.TargetMember, newInstance)); } return(Expression.Block ( Expression.Assign(context.TargetMember, context.TargetMemberValueGetter), Expression.IfThen ( Expression.Equal(context.TargetMember, context.TargetMemberNullValue), Expression.Assign(context.TargetMember, newInstance) ) )); }
public virtual Expression GetMemberAssignment(MemberMappingContext context) { Expression newInstance = this.GetMemberNewInstance(context); bool isCreateNewInstance = context.Options.ReferenceBehavior == ReferenceBehaviors.CREATE_NEW_INSTANCE; if (isCreateNewInstance || context.TargetMemberValueGetter == null) { return(Expression.Assign(context.TargetMember, newInstance)); } return(Expression.Block ( Expression.Assign(context.TargetMember, context.TargetMemberValueGetter), Expression.IfThen ( Expression.Equal(context.TargetMember, context.TargetMemberNullValue), Expression.Assign(context.TargetMember, newInstance) ) )); }
/// <summary> /// Returns an expression calling Expression.New. /// Expression.New will call a constructor intializing the capacity of the collection /// </summary> protected virtual Expression GetNewInstanceWithReservedCapacity(MemberMappingContext context) { var constructorWithCapacity = context.TargetMember.Type.GetConstructor(new Type[] { typeof(int) }); if (constructorWithCapacity == null) { return(null); } var getCountMethod = GetCountMethod(context.SourceMember.Type); Expression getCountMethodCallExp; if (getCountMethod.IsStatic) { getCountMethodCallExp = Expression.Call(null, getCountMethod, context.SourceMember); } else { getCountMethodCallExp = Expression.Call(context.SourceMember, getCountMethod); } return(Expression.New(constructorWithCapacity, getCountMethodCallExp)); }
public override Expression GetMemberAssignment(MemberMappingContext context) { //FOR ARRAYS WE ALSO CHECK IF THE TARGET ARRAY IS LARGE ENOUGH //TO HOLD ALL OF THE ITEMS OF THE SOURCE COLLECTION. //IF THE ARRAY IS NOT LARGE ENOUGH, WE CREATE A NEW INSTANCE LARGE ENOUGH. Expression newInstance = this.GetMemberNewInstance(context); var sourceCountMethod = this.GetCountMethod(context.SourceMember.Type); var targetCountMethod = this.GetCountMethod(context.TargetMember.Type); return(Expression.Block ( base.GetMemberAssignment(context), Expression.IfThen ( Expression.LessThan(Expression.Call(context.TargetMember, targetCountMethod), Expression.Call(context.SourceMember, sourceCountMethod)), Expression.Assign(context.TargetMember, newInstance) ) )); }
protected virtual Expression GetMemberNewInstance(MemberMappingContext context) { if (context.Options.CustomTargetConstructor != null) { return(Expression.Invoke(context.Options.CustomTargetConstructor)); } //If we are just cloning (ie: mapping on the same type) we prefer to use exactly the //same runtime-type used in the source (in order to manage abstract classes, interfaces and inheritance). if (context.TargetMember.Type.IsAssignableFrom(context.SourceMember.Type)) { MethodInfo getTypeMethodInfo = typeof(object).GetMethod(nameof(object.GetType)); var getSourceType = Expression.Call(context.SourceMemberValueGetter, getTypeMethodInfo); return(Expression.Convert(Expression.Call(null, typeof(InstanceFactory).GetMethods()[1], getSourceType, Expression.Constant(null, typeof(object[]))), context.TargetMember.Type)); } var defaultCtor = context.TargetMember.Type.GetConstructor(Type.EmptyTypes); if (defaultCtor != null) { return(Expression.New(context.TargetMember.Type)); } if (context.TargetMember.Type.IsInterface) { throw new Exception($"Type {context.TargetMember.Type} is an interface but what type to use cannot be inferred. " + $"Please configure what type to use like this: cfg.MapTypes<IA, IB>( () => new ConcreteTypeBImplementingIB() ) "); } else { throw new Exception($"Type {context.TargetMember.Type} does not have a default constructor. " + $"Please provide a way to construct the type like this: cfg.MapTypes<A, B>( () => new B(param1,param2,...) ) "); } }
public override Expression GetMemberNewInstance(MemberMappingContext context, out bool isMapCompleted) { isMapCompleted = false; if (context.Options.CustomTargetConstructor != null) { return(Expression.Invoke(context.Options.CustomTargetConstructor)); } var collectionContext = new CollectionMapperContext((Mapping)context.Options); if (context.TargetMember.Type.IsArray) { var sourceCountMethod = GetCountMethod(context.SourceMember.Type); Expression sourceCountMethodCallExp; if (sourceCountMethod.IsStatic) { sourceCountMethodCallExp = Expression.Call(null, sourceCountMethod, context.SourceMember); } else { sourceCountMethodCallExp = Expression.Call(context.SourceMember, sourceCountMethod); } return(Expression.NewArrayInit(context.TargetMember.Type, sourceCountMethodCallExp)); } //OPTIMIZATION: If the types involved are primitives of exactly the same type //we can use the constructor taking as input the collection and avoid recursion if ((collectionContext.IsSourceElementTypeBuiltIn || collectionContext.IsTargetElementTypeBuiltIn) && collectionContext.SourceCollectionElementType == collectionContext.TargetCollectionElementType && context.Options.ReferenceBehavior == ReferenceBehaviors.CREATE_NEW_INSTANCE) { var newInstance = GetNewInstanceFromSourceCollection(context, collectionContext); if (newInstance != null) { var typeMapping = MapperConfiguration[context.SourceMember.Type, context.TargetMember.Type]; //We do not want recursion on each collection's item //but Capacity and other collection members must be mapped. Expression memberMappings = Expression.Empty(); if (context.TargetMemberValueGetter != null) //we can only map subparam if a way to access subparam is provided/resolved. Edge case is: providing a member's setter method but not the getter's { memberMappings = this.GetMemberMappingsExpression(typeMapping) .ReplaceParameter(context.Mapper, context.Mapper.Name) .ReplaceParameter(context.ReferenceTracker, context.ReferenceTracker.Name) .ReplaceParameter(context.SourceMember, context.SourceInstance.Name) .ReplaceParameter(context.TargetMember, context.TargetInstance.Name); } isMapCompleted = true; //we created a new instance also passing source array of non-reference type that will be copied return(Expression.Block ( Expression.IfThenElse ( Expression.IsTrue(Expression.Equal(context.SourceMemberValueGetter, context.SourceMemberNullValue)), //Expression.Assign( context.TargetMember, context.TargetMemberNullValue ), //this only works for properties/fields context.TargetMemberValueSetter .ReplaceParameter(context.TargetMemberNullValue, "targetValue"), //works on setter methods too Expression.Block ( //in order to assign inner members we need to assign TargetMember //(we also replaced TargetInstance with TargetMember) context.TargetMemberValueSetter .ReplaceParameter(newInstance, "targetValue"), //works on setter methods too //Expression.Assign( context.TargetMember, newInstance ), //this only works for properties/fields memberMappings ?? Expression.Empty() ) ) )); } } //OPTIMIZATION: if we need to create a new instance of a collection //we can try to reserve just the right capacity thus avoiding reallocations. //If the source collection implements ICollection we can read 'Count' property without any iteration. if (context.Options.ReferenceBehavior == ReferenceBehaviors.CREATE_NEW_INSTANCE && context.SourceMember.Type.ImplementsInterface(typeof(ICollection <>))) { var newInstanceWithReservedCapacity = this.GetNewInstanceWithReservedCapacity(context); if (newInstanceWithReservedCapacity != null) { return(newInstanceWithReservedCapacity); } } //DEALING WITH INTERFACES Type sourceType = context.SourceMember.Type.IsGenericType ? context.SourceMember.Type.GetGenericTypeDefinition() : context.SourceMember.Type; Type targetType = context.TargetMember.Type.IsGenericType ? context.TargetMember.Type.GetGenericTypeDefinition() : context.TargetMember.Type; //If we are just cloning (ie: mapping on the same type) we prefer to use exactly the //same runtime-type used in the source (in order to manage abstract classes, interfaces and inheritance). if (context.TargetMember.Type.IsInterface && (context.TargetMember.Type.IsAssignableFrom(context.SourceMember.Type) || targetType.IsAssignableFrom(sourceType) || sourceType.ImplementsInterface(targetType))) { ////RUNTIME INSPECTION (in order to use on the target the same type of the source, if possible) ////MethodInfo getTypeMethodInfo = typeof( object ).GetMethod( nameof( object.GetType ) ); ////var getSourceType = Expression.Call( context.SourceMemberValueGetter, getTypeMethodInfo ); ////return Expression.Convert( Expression.Call( null, typeof( InstanceFactory ).GetMethods()[ 1 ], //// getSourceType, Expression.Constant( null, typeof( object[] ) ) ), context.TargetMember.Type ); //Runtime inspection did not work well between array and collection backed by ICollection or IEnumerable; //just provide a list if the target is backed by an interface... return(Expression.New(typeof(List <>).MakeGenericType(collectionContext.TargetCollectionElementType))); } var defaultCtor = targetType.GetConstructor(Type.EmptyTypes); if (defaultCtor != null) { return(Expression.New(context.TargetMember.Type)); } if (targetType.IsInterface) { //use List<> as default collection type return(Expression.New(typeof(List <>).MakeGenericType(collectionContext.TargetCollectionElementType))); } throw new Exception($"Type {targetType} does not have a default constructor. " + $"Please provide a way to construct the type like this: cfg.MapTypes<A, B>( () => new B(param1,param2,...) ) "); }
private LambdaExpression ToActionWithReferenceTrackerLambda(Expression expression, MemberMappingContext memberContext) { var delegateType = typeof(Action <, ,>).MakeGenericType( memberContext.ReferenceTracker.Type, memberContext.SourceInstance.Type, memberContext.TargetInstance.Type); return(Expression.Lambda(delegateType, expression, memberContext.ReferenceTracker, memberContext.SourceInstance, memberContext.TargetInstance)); }
private LambdaExpression GetComplexMemberExpression(MemberMapping mapping) { var memberContext = new MemberMappingContext(mapping); if (mapping.CustomConverter != null) { var targetSetterInstanceParamName = mapping.TargetMember.ValueSetter.Parameters[0].Name; var targetSetterValueParamName = mapping.TargetMember.ValueSetter.Parameters[1].Name; var valueReaderExp = Expression.Invoke(mapping.CustomConverter, memberContext.SourceMemberValueGetter); var exp = mapping.TargetMember.ValueSetter.Body .ReplaceParameter(memberContext.TargetInstance, targetSetterInstanceParamName) .ReplaceParameter(valueReaderExp, targetSetterValueParamName); return(ToActionWithReferenceTrackerLambda(exp, memberContext)); } var memberAssignmentExp = ((IMemberMappingExpression)mapping.Mapper) .GetMemberAssignment(memberContext, out bool needsTrackingOrRecursion); if (!needsTrackingOrRecursion) { var exp = memberAssignmentExp .ReplaceParameter(memberContext.SourceMemberValueGetter, "sourceValue"); //if a setter method was provided or resolved a target value getter may be missing if (memberContext.TargetMemberValueGetter != null) { exp = exp.ReplaceParameter(memberContext.TargetMemberValueGetter, "targetValue"); } else // if( memberContext.TargetMemberValueSetter != null ) fails directly if not resolved/provided { exp = exp.ReplaceParameter(memberContext.TargetMemberValueSetter, "targetValue"); } return(ToActionWithReferenceTrackerLambda(exp, memberContext)); } if (memberContext.Options.IsReferenceTrackingEnabled) { var parameters = new List <ParameterExpression>() { memberContext.SourceMember, memberContext.TargetMember, memberContext.TrackedReference }; var exp = Expression.Block ( parameters, Expression.Assign(memberContext.SourceMember, memberContext.SourceMemberValueGetter), ReferenceTrackingExpression.GetMappingExpression ( memberContext.ReferenceTracker, memberContext.SourceMember, memberContext.TargetMember, memberAssignmentExp, memberContext.Mapper, _mapper, Expression.Constant(mapping) ), memberContext.TargetMemberValueSetter ); return(ToActionWithReferenceTrackerLambda(exp, memberContext)); } else { var mapMethod = ReferenceMapperContext.RecursiveMapMethodInfo .MakeGenericMethod(memberContext.SourceMember.Type, memberContext.TargetMember.Type); var exp = Expression.Block ( memberAssignmentExp .ReplaceParameter(memberContext.SourceMemberValueGetter, "sourceValue") .ReplaceParameter(memberContext.TargetMemberValueGetter, "targetValue"), Expression.Call(memberContext.Mapper, mapMethod, memberContext.SourceMemberValueGetter, memberContext.TargetMemberValueGetter, memberContext.ReferenceTracker, Expression.Constant(mapping)) ); return(ToActionWithReferenceTrackerLambda(exp, memberContext)); } }
public override Expression GetMemberNewInstance(MemberMappingContext context, out bool isMapComplete) { isMapComplete = false; //1. Create a new temporary collection passing source as input //2. Read items from the newly created temporary collection and add items to the target var collectionContext = (CollectionMapperContext)GetMapperContext((Mapping)context.Options); var tempCollectionType = this.GetTemporaryCollectionType(collectionContext); var tempCollectionConstructorInfo = tempCollectionType.GetConstructor(Type.EmptyTypes); var tempCollection = Expression.Parameter(tempCollectionType, "tempCollection"); var newTempCollectionExp = Expression.New(tempCollectionConstructorInfo); var newTargetCtor = context.TargetMember.Type.GetConstructors().First(ctor => { var parameters = ctor.GetParameters(); if (parameters.Length != 1) { return(false); } return(parameters[0].ParameterType.IsEnumerable()); }); var temporaryCollectionInsertionMethod = this.GetTemporaryCollectionInsertionMethod(collectionContext); if (collectionContext.IsTargetElementTypeBuiltIn) { return(Expression.Block ( new[] { tempCollection }, Expression.Assign(tempCollection, newTempCollectionExp), SimpleCollectionLoop ( context.SourceMember, collectionContext.SourceCollectionElementType, tempCollection, collectionContext.TargetCollectionElementType, temporaryCollectionInsertionMethod, collectionContext.SourceCollectionLoopingVar, collectionContext.Mapper, collectionContext.ReferenceTracker ), Expression.New(newTargetCtor, tempCollection) )); } return(Expression.Block ( new[] { tempCollection }, Expression.Assign(tempCollection, newTempCollectionExp), ComplexCollectionLoop ( context.SourceMember, collectionContext.SourceCollectionElementType, tempCollection, collectionContext.TargetCollectionElementType, temporaryCollectionInsertionMethod, collectionContext.SourceCollectionLoopingVar, context.ReferenceTracker, context.Mapper, collectionContext ), Expression.New(newTargetCtor, tempCollection) )); }
public virtual Expression GetMemberNewInstance(MemberMappingContext context, out bool isMapComplete) { isMapComplete = false; return(GetMemberNewInstanceInternal(context.SourceMemberValueGetter, context.SourceMember.Type, context.TargetMember.Type, context.Options)); }
public override Expression GetMemberNewInstance(MemberMappingContext context, out bool isMapComplete) { isMapComplete = false; return(this.GetNewInstanceWithReservedCapacity(context)); }
protected override Expression GetMemberNewInstance(MemberMappingContext context) { return(this.GetNewInstanceWithReservedCapacity(context)); }
protected override Expression GetMemberNewInstance(MemberMappingContext context) { if (context.Options.CustomTargetConstructor != null) { return(Expression.Invoke(context.Options.CustomTargetConstructor)); } var collectionContext = new CollectionMapperContext(context.SourceMember.Type, context.TargetMember.Type, context.Options); //OPTIMIZATION: If the types involved are primitives of exactly the same type //we can use the constructor taking as input the collection and avoid recursion if ((collectionContext.IsSourceElementTypeBuiltIn || collectionContext.IsTargetElementTypeBuiltIn) && collectionContext.SourceCollectionElementType == collectionContext.TargetCollectionElementType && context.Options.ReferenceBehavior == ReferenceBehaviors.CREATE_NEW_INSTANCE) { var newInstance = GetNewInstanceFromSourceCollection(context, collectionContext); if (newInstance != null) { var typeMapping = MapperConfiguration[context.SourceMember.Type, context.TargetMember.Type]; //We do not want recursion on each collection's item //but Capacity and other collection members must be mapped. var memberMappings = this.GetMemberMappings(typeMapping) .ReplaceParameter(context.Mapper, context.Mapper.Name) .ReplaceParameter(context.ReferenceTracker, context.ReferenceTracker.Name) .ReplaceParameter(context.SourceMember, context.SourceInstance.Name) .ReplaceParameter(context.TargetMember, context.TargetInstance.Name); context.InitializationComplete = true; return(Expression.Block ( //in order to assign inner members we need to assign TargetMember //(we also replaced TargetInstance with TargetMember) Expression.Assign(context.TargetMember, newInstance), memberMappings, context.TargetMember )); } } //OPTIMIZATION: if we need to create a new instance of a collection //we can try to reserve just the right capacity thus avoiding reallocations. //If the source collection implements ICollection we can read 'Count' property without any iteration. if (context.Options.ReferenceBehavior == ReferenceBehaviors.CREATE_NEW_INSTANCE && context.SourceMember.Type.ImplementsInterface(typeof(ICollection <>))) { var newInstanceWithReservedCapacity = this.GetNewInstanceWithReservedCapacity(context); if (newInstanceWithReservedCapacity != null) { return(newInstanceWithReservedCapacity); } } //DEALING WITH INTERFACES Type sourceType = context.SourceMember.Type.IsGenericType ? context.SourceMember.Type.GetGenericTypeDefinition() : context.SourceMember.Type; Type targetType = context.TargetMember.Type.IsGenericType ? context.TargetMember.Type.GetGenericTypeDefinition() : context.TargetMember.Type; //If we are just cloning (ie: mapping on the same type) we prefer to use exactly the //same runtime-type used in the source (in order to manage abstract classes, interfaces and inheritance). if (context.TargetMember.Type.IsInterface && (context.TargetMember.Type.IsAssignableFrom(context.SourceMember.Type) || targetType.IsAssignableFrom(sourceType) || sourceType.ImplementsInterface(targetType))) { ////RUNTIME INSPECTION (in order to use on the target the same type of the source, if possible) ////MethodInfo getTypeMethodInfo = typeof( object ).GetMethod( nameof( object.GetType ) ); ////var getSourceType = Expression.Call( context.SourceMemberValueGetter, getTypeMethodInfo ); ////return Expression.Convert( Expression.Call( null, typeof( InstanceFactory ).GetMethods()[ 1 ], //// getSourceType, Expression.Constant( null, typeof( object[] ) ) ), context.TargetMember.Type ); //Runtime inspection did not work well between array and collection backed by ICollection or IEnumerable; //just provide a list if the target is backed by an interface... return(Expression.New(typeof(List <>).MakeGenericType(collectionContext.TargetCollectionElementType))); } return(Expression.New(context.TargetMember.Type)); }
private Expression GetComplexMemberExpression(MemberMapping mapping) { /* SOURCE (NULL) -> TARGET = NULL * * SOURCE (NOT NULL / VALUE ALREADY TRACKED) -> TARGET (NULL) = ASSIGN TRACKED OBJECT * SOURCE (NOT NULL / VALUE ALREADY TRACKED) -> TARGET (NOT NULL) = ASSIGN TRACKED OBJECT (the priority is to map identically the source to the target) * * SOURCE (NOT NULL / VALUE UNTRACKED) -> TARGET (NULL) = ASSIGN NEW OBJECT * SOURCE (NOT NULL / VALUE UNTRACKED) -> TARGET (NOT NULL) = KEEP USING INSTANCE OR CREATE NEW INSTANCE */ var memberContext = new MemberMappingContext(mapping); if (mapping.CustomConverter != null) { var targetSetterInstanceParamName = mapping.TargetMember.ValueSetter.Parameters[0].Name; var targetSetterValueParamName = mapping.TargetMember.ValueSetter.Parameters[1].Name; var valueReaderExp = Expression.Invoke(mapping.CustomConverter, memberContext.SourceMemberValueGetter); return(mapping.TargetMember.ValueSetter.Body .ReplaceParameter(memberContext.TargetInstance, targetSetterInstanceParamName) .ReplaceParameter(valueReaderExp, targetSetterValueParamName) .ReplaceParameter(valueReaderExp, mapping.CustomConverter.Parameters[0].Name)); } var mapMethod = ReferenceMapperContext.RecursiveMapMethodInfo.MakeGenericMethod( memberContext.SourceMember.Type, memberContext.TargetMember.Type); Expression itemLookupCall = Expression.Call ( Expression.Constant(refTrackingLookup.Target), refTrackingLookup.Method, memberContext.ReferenceTracker, memberContext.SourceMember, Expression.Constant(memberContext.TargetMember.Type) ); Expression itemCacheCall = Expression.Call ( Expression.Constant(addToTracker.Target), addToTracker.Method, memberContext.ReferenceTracker, memberContext.SourceMember, Expression.Constant(memberContext.TargetMember.Type), memberContext.TargetMember ); return(Expression.Block ( new[] { memberContext.TrackedReference, memberContext.SourceMember, memberContext.TargetMember }, Expression.Assign(memberContext.SourceMember, memberContext.SourceMemberValueGetter), Expression.IfThenElse ( Expression.Equal(memberContext.SourceMember, memberContext.SourceMemberNullValue), Expression.Assign(memberContext.TargetMember, memberContext.TargetMemberNullValue), Expression.Block ( //object lookup. An intermediate variable (TrackedReference) is needed in order to deal with ReferenceMappingStrategies Expression.Assign(memberContext.TrackedReference, Expression.Convert(itemLookupCall, memberContext.TargetMember.Type)), Expression.IfThenElse ( Expression.NotEqual(memberContext.TrackedReference, memberContext.TargetMemberNullValue), Expression.Assign(memberContext.TargetMember, memberContext.TrackedReference), Expression.Block ( ((IMemberMappingExpression)mapping.Mapper) .GetMemberAssignment(memberContext), //cache reference itemCacheCall, memberContext.InitializationComplete ? (Expression)Expression.Empty() : Expression.Call(memberContext.Mapper, mapMethod, memberContext.SourceMember, memberContext.TargetMember, memberContext.ReferenceTracker, Expression.Constant(mapping)) ) ) ) ), memberContext.TargetMemberValueSetter )); }
private Expression GetComplexMemberExpression(MemberMapping mapping) { /* SOURCE (NULL) -> TARGET = NULL * * SOURCE (NOT NULL / VALUE ALREADY TRACKED) -> TARGET (NULL) = ASSIGN TRACKED OBJECT * SOURCE (NOT NULL / VALUE ALREADY TRACKED) -> TARGET (NOT NULL) = ASSIGN TRACKED OBJECT (the priority is to map identically the source to the target) * * SOURCE (NOT NULL / VALUE UNTRACKED) -> TARGET (NULL) = ASSIGN NEW OBJECT * SOURCE (NOT NULL / VALUE UNTRACKED) -> TARGET (NOT NULL) = KEEP USING INSTANCE OR CREATE NEW INSTANCE */ var memberContext = new MemberMappingContext(mapping); var mapMethod = MemberMappingContext.RecursiveMapMethodInfo.MakeGenericMethod( memberContext.SourceMember.Type, memberContext.TargetMember.Type); Expression itemLookupCall = Expression.Call ( Expression.Constant(refTrackingLookup.Target), refTrackingLookup.Method, memberContext.ReferenceTracker, memberContext.SourceMember, Expression.Constant(memberContext.TargetMember.Type) ); Expression itemCacheCall = Expression.Call ( Expression.Constant(addToTracker.Target), addToTracker.Method, memberContext.ReferenceTracker, memberContext.SourceMember, Expression.Constant(memberContext.TargetMember.Type), memberContext.TargetMember ); return(Expression.Block ( new[] { memberContext.TrackedReference, memberContext.SourceMember, memberContext.TargetMember }, Expression.Assign(memberContext.SourceMember, memberContext.SourceMemberValueGetter), Expression.IfThenElse ( Expression.Equal(memberContext.SourceMember, memberContext.SourceMemberNullValue), Expression.Assign(memberContext.TargetMember, memberContext.TargetMemberNullValue), Expression.Block ( //object lookup. An intermediate variable (TrackedReference) is needed in order to deal with ReferenceMappingStrategies Expression.Assign(memberContext.TrackedReference, Expression.Convert(itemLookupCall, memberContext.TargetMember.Type)), Expression.IfThenElse ( Expression.NotEqual(memberContext.TrackedReference, memberContext.TargetMemberNullValue), Expression.Assign(memberContext.TargetMember, memberContext.TrackedReference), Expression.Block ( this.GetTargetInstanceAssignment(memberContext, mapping), //cache reference itemCacheCall, Expression.Call(memberContext.Mapper, mapMethod, memberContext.SourceMember, memberContext.TargetMember, memberContext.ReferenceTracker, Expression.Constant(mapping)) ) ) ) ), memberContext.TargetMemberValueSetter )); }