예제 #1
0
        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)));
        }
예제 #5
0
        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)
                       )
                   ));
        }
예제 #6
0
        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));
        }
예제 #7
0
        /// <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)));
        }
예제 #8
0
        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)
                       )
                   ));
        }
예제 #11
0
        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));
        }
예제 #12
0
        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)
                       )
                   ));
        }
예제 #13
0
        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)
                       )
                   ));
        }
예제 #14
0
        /// <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));
        }
예제 #15
0
        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)
                       )
                   ));
        }
예제 #16
0
        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,...) ) ");
            }
        }
예제 #17
0
        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,...) ) ");
        }
예제 #18
0
        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));
        }
예제 #19
0
        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));
            }
        }
예제 #20
0
        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)
                   ));
        }
예제 #21
0
 public virtual Expression GetMemberNewInstance(MemberMappingContext context, out bool isMapComplete)
 {
     isMapComplete = false;
     return(GetMemberNewInstanceInternal(context.SourceMemberValueGetter,
                                         context.SourceMember.Type, context.TargetMember.Type, context.Options));
 }
예제 #22
0
 public override Expression GetMemberNewInstance(MemberMappingContext context, out bool isMapComplete)
 {
     isMapComplete = false;
     return(this.GetNewInstanceWithReservedCapacity(context));
 }
예제 #23
0
 protected override Expression GetMemberNewInstance(MemberMappingContext context)
 {
     return(this.GetNewInstanceWithReservedCapacity(context));
 }
예제 #24
0
        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));
        }
예제 #25
0
        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
                   ));
        }