protected Expression SimpleCollectionLoop(CollectionMapperContext context,
                                                  ParameterExpression sourceCollection, ParameterExpression targetCollection)
        {
            var targetCollectionInsertionMethod = GetTargetCollectionInsertionMethod(context);

            if (targetCollectionInsertionMethod == null)
            {
                string msg = $@"'{nameof( context.TargetInstance.Type )}' does not provide an insertion method. " +
                             $"Please override '{nameof( GetTargetCollectionInsertionMethod )}' to provide the item insertion method.";

                throw new Exception(msg);
            }

            var itemMapping = MapperConfiguration[context.SourceCollectionElementType,
                                                  context.TargetCollectionElementType].MappingExpression;

            Expression loopBody = Expression.Call
                                  (
                targetCollection, targetCollectionInsertionMethod,
                itemMapping.Body.ReplaceParameter(
                    context.SourceCollectionLoopingVar, itemMapping.Parameters[0].Name)
                                  );

            return(ExpressionLoops.ForEach(sourceCollection,
                                           context.SourceCollectionLoopingVar, loopBody));
        }
        public Expression CollectionLoopWithReferenceTracking(CollectionMapperContext context,
                                                              ParameterExpression sourceCollection, ParameterExpression targetCollection)
        {
            var targetCollectionInsertionMethod = GetTargetCollectionInsertionMethod(context);

            if (targetCollectionInsertionMethod == null)
            {
                string msg = $@"'{nameof( context.TargetInstance.Type )}' does not provide an insertion method. " +
                             $"Please override '{nameof( GetTargetCollectionInsertionMethod )}' to provide the item insertion method.";

                throw new Exception(msg);
            }

            var itemMapping = MapperConfiguration[context.SourceCollectionLoopingVar.Type,
                                                  context.TargetCollectionElementType].MappingExpression;

            var newElement = Expression.Variable(context.TargetCollectionElementType, "newElement");

            return(Expression.Block
                   (
                       new[] { newElement },

                       ExpressionLoops.ForEach(sourceCollection, context.SourceCollectionLoopingVar, Expression.Block
                                               (
                                                   LookUpBlock(context, context.SourceCollectionLoopingVar, newElement),
                                                   Expression.Call(targetCollection, targetCollectionInsertionMethod, newElement)
                                               )
                                               )));
        }
示例#3
0
        protected override MethodInfo GetTargetCollectionInsertionMethod(CollectionMapperContext context)
        {
            //It is forbidden to use nameof with unbound generic types. We use 'int' just to get around that.
            var methodName   = nameof(LinkedList <int> .AddLast);
            var methodParams = new[] { context.TargetCollectionElementType };

            return(context.TargetInstance.Type.GetMethod(methodName, methodParams));
        }
示例#4
0
        /// <summary>
        /// Returns the method that allows to clear the target collection.
        /// </summary>
        protected virtual MethodInfo GetTargetCollectionClearMethod(CollectionMapperContext context)
        {
            if (context.TargetInstance.Type.IsArray)
            {
                return(typeof(Array).GetMethod(nameof(Array.Clear),
                                               BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy));
            }

            //It is forbidden to use nameof with unbound generic types. We use 'int' just to get around that.
            return(context.TargetInstance.Type.GetMethod(nameof(ICollection <int> .Clear)));
        }
示例#5
0
        protected override Expression GetTargetCollectionClearExpression(CollectionMapperContext context)
        {
            bool isResetCollection = /*context.Options.ReferenceBehavior == ReferenceBehaviors.USE_TARGET_INSTANCE_IF_NOT_NULL && */
                                     context.Options.CollectionBehavior == CollectionBehaviors.RESET;

            var clearMethod = GetTargetCollectionClearMethod(context);

            //var lengthProperty = context.TargetInstance.Type.GetProperty( nameof( Array.Length ) );

            return(isResetCollection ? Expression.Call(null, clearMethod, context.TargetInstance,
                                                       Expression.Constant(0, typeof(int)), Expression.ArrayLength(context.TargetInstance))
                    : (Expression)Expression.Empty());
        }
示例#6
0
 protected override MethodInfo GetUpdateCollectionMethod(CollectionMapperContext context)
 {
     return(typeof(LinqExtensions).GetMethod
            (
                nameof(LinqExtensions.ArrayUpdate),
                BindingFlags.Static | BindingFlags.Public
            )
            .MakeGenericMethod
            (
                context.SourceCollectionElementType,
                context.TargetCollectionElementType
            ));
 }
示例#7
0
        protected virtual Expression GetUpdateCollectionExpression(CollectionMapperContext context)
        {
            if (context.Options.CollectionItemEqualityComparer == null)
            {
                return(Expression.Empty());
            }

            var updateCollectionMethodInfo = GetUpdateCollectionMethod(context);

            return(Expression.Call(null, updateCollectionMethodInfo, context.Mapper,
                                   context.ReferenceTracker, context.SourceInstance, context.TargetInstance,
                                   Expression.Convert(Expression.Constant(context.Options.CollectionItemEqualityComparer.Compile()),
                                                      typeof(Func <, ,>).MakeGenericType(context.SourceCollectionElementType, context.TargetCollectionElementType, typeof(bool)))));
        }
        public BlockExpression LookUpBlock(CollectionMapperContext context,
                                           ParameterExpression sourceParam, ParameterExpression targetParam)
        {
            Expression itemLookupCall = Expression.Call
                                        (
                Expression.Constant(refTrackingLookup.Target),
                refTrackingLookup.Method,
                context.ReferenceTracker,
                sourceParam,
                Expression.Constant(targetParam.Type)
                                        );

            Expression itemCacheCall = Expression.Call
                                       (
                Expression.Constant(addToTracker.Target),
                addToTracker.Method,
                context.ReferenceTracker,
                sourceParam,
                Expression.Constant(targetParam.Type),
                targetParam
                                       );

            var mapMethod = CollectionMapperContext.RecursiveMapMethodInfo
                            .MakeGenericMethod(sourceParam.Type, targetParam.Type);

            var itemMapping = MapperConfiguration[sourceParam.Type, targetParam.Type];

            return(Expression.Block
                   (
                       Expression.Assign(targetParam, Expression.Convert(itemLookupCall, targetParam.Type)),

                       Expression.IfThen
                       (
                           Expression.Equal(targetParam, Expression.Constant(null, targetParam.Type)),

                           Expression.Block
                           (
                               Expression.Assign(targetParam, Expression.New(targetParam.Type)),

                               itemCacheCall,

                               Expression.Call(context.Mapper, mapMethod, sourceParam, targetParam,
                                               context.ReferenceTracker, Expression.Constant(itemMapping))
                           )
                       )
                   ));
        }
示例#9
0
        /// <summary>
        /// Returns the expression that clears the collection
        /// </summary>
        protected virtual Expression GetTargetCollectionClearExpression(CollectionMapperContext context)
        {
            bool isResetCollection = context.Options.ReferenceBehavior == ReferenceBehaviors.USE_TARGET_INSTANCE_IF_NOT_NULL &&
                                     context.Options.CollectionBehavior == CollectionBehaviors.RESET;

            if (isResetCollection)
            {
                var clearMethod = GetTargetCollectionClearMethod(context);
                if (clearMethod == null && isResetCollection)
                {
                    string msg = $@"Cannot reset the collection. Type '{context.TargetInstance.Type}' does not provide a Clear method";
                    throw new Exception(msg);
                }

                return(Expression.Call(context.TargetInstance, clearMethod));
            }

            return(Expression.Empty());
        }
 /// <summary>
 /// Returns the method that allows to insert items in the target collection.
 /// </summary>
 protected virtual MethodInfo GetTargetCollectionInsertionMethod(CollectionMapperContext context)
 {
     //It is forbidden to use nameof with unbound generic types. We use 'int' just to get around that.
     return(context.TargetInstance.Type.GetMethod(nameof(ICollection <int> .Add)));
 }
 /// <summary>
 /// Returns the method that allows to clear the target collection.
 /// </summary>
 private MethodInfo GetTargetCollectionClearMethod(CollectionMapperContext context)
 {
     //It is forbidden to use nameof with unbound generic types. We use 'int' just to get around that.
     return(context.TargetInstance.Type.GetMethod(nameof(ICollection <int> .Clear)));
 }
 protected virtual Type GetTemporaryCollectionType(CollectionMapperContext context)
 {
     return(typeof(List <>).MakeGenericType(context.SourceCollectionElementType));
 }
 protected virtual MethodInfo GetTemporaryCollectionInsertionMethod(CollectionMapperContext context)
 {
     return(this.GetTemporaryCollectionType(context).GetMethod(nameof(List <int> .Add)));
 }
示例#14
0
        protected override MethodInfo GetTargetCollectionClearMethod(CollectionMapperContext context)
        {
            var paramTypes = new[] { typeof(Array), typeof(int), typeof(int) };

            return(typeof(Array).GetMethod(nameof(Array.Clear), paramTypes));
        }
示例#15
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,...) ) ");
        }
示例#16
0
 protected override MethodInfo GetTargetCollectionInsertionMethod(CollectionMapperContext context)
 {
     return(context.TargetInstance.Type.GetMethod("Push"));
 }
示例#17
0
        /// <summary>
        /// Returns an expression calling Expression.New.
        /// Expression.New will call a constructor taking as input a collection
        /// </summary>
        protected virtual Expression GetNewInstanceFromSourceCollection(MemberMappingContext context, CollectionMapperContext collectionContext)
        {
            var targetConstructor = context.TargetMember.Type.GetConstructor(
                new[] { typeof(IEnumerable <>).MakeGenericType(collectionContext.TargetCollectionElementType) });

            if (targetConstructor == null)
            {
                return(null);
            }
            return(Expression.New(targetConstructor, context.SourceMember));
        }
示例#18
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));
        }
示例#19
0
 protected override Type GetTemporaryCollectionType(CollectionMapperContext context)
 {
     //by copying data in a temp stack and then in the target collection
     //the correct order of the items is preserved
     return(typeof(Stack <>).MakeGenericType(context.SourceCollectionElementType));
 }
示例#20
0
 protected override Type GetTemporaryCollectionType(CollectionMapperContext context)
 {
     return(typeof(Stack <>).MakeGenericType(context.SourceCollectionElementType));
 }
示例#21
0
 protected override MethodInfo GetTargetCollectionClearMethod(CollectionMapperContext context)
 {
     return(typeof(Array).GetMethod(nameof(Array.Clear),
                                    BindingFlags.Public | BindingFlags.Static));
 }
示例#22
0
        protected override Expression ComplexCollectionLoop(ParameterExpression sourceCollection, Type sourceCollectionElementType,
                                                            ParameterExpression targetCollection, Type targetCollectionElementType,
                                                            MethodInfo targetCollectionInsertionMethod, ParameterExpression sourceCollectionLoopingVar,
                                                            ParameterExpression referenceTracker, ParameterExpression mapper, CollectionMapperContext context = null)
        {
            var newElement = Expression.Variable(targetCollectionElementType, "newElement");
            var itemIndex  = Expression.Parameter(typeof(int), "itemIndex");

            return(Expression.Block
                   (
                       new[] { newElement, itemIndex },

                       ExpressionLoops.ForEach(sourceCollection, sourceCollectionLoopingVar, Expression.Block
                                               (
                                                   LookUpBlock(sourceCollectionLoopingVar, newElement, referenceTracker, mapper),
                                                   Expression.Assign(Expression.ArrayAccess(targetCollection, itemIndex), newElement),

                                                   Expression.AddAssign(itemIndex, Expression.Constant(1))
                                               )
                                               )));
        }
示例#23
0
        protected virtual Expression ComplexCollectionLoop(ParameterExpression sourceCollection, Type sourceCollectionElementType,
                                                           ParameterExpression targetCollection, Type targetCollectionElementType,
                                                           MethodInfo targetCollectionInsertionMethod, ParameterExpression sourceCollectionLoopingVar,
                                                           ParameterExpression referenceTracker, ParameterExpression mapper, CollectionMapperContext context = null)
        {
            if (targetCollectionInsertionMethod == null)
            {
                string msg = $@"'{targetCollection.Type}' does not provide an insertion method. " +
                             $"Please override '{nameof( GetTargetCollectionInsertionMethod )}' to provide the item insertion method.";

                throw new Exception(msg);
            }

            var newElement = Expression.Variable(targetCollectionElementType, "newElement");

            var mapping = ((Mapping)context.Options).GlobalConfig[sourceCollectionElementType, targetCollectionElementType];

            if (mapping.Source.EntryType != mapping.Source.ReturnType)
            {
                mapping = ((Mapping)context.Options).GlobalConfig[mapping.Source.ReturnType, targetCollectionElementType];
            }

            var valueGetter = mapping.Source.ValueGetter;

            /*member extraction support*/
            Expression valueExtraction = Expression.Invoke(valueGetter, sourceCollectionLoopingVar);

            if (((Mapping)context.Options).Source.MemberAccessPath.Count <= 1)
            {
                valueExtraction = sourceCollectionLoopingVar;
            }

            return(Expression.Block
                   (
                       new[] { newElement },

                       ExpressionLoops.ForEach(sourceCollection, sourceCollectionLoopingVar, Expression.Block
                                               (
                                                   Expression.IfThenElse
                                                   (
                                                       Expression.Equal(valueExtraction, Expression.Constant(null, sourceCollectionElementType)),

                                                       Expression.Call(targetCollection, targetCollectionInsertionMethod, Expression.Default(targetCollectionElementType)),

                                                       Expression.Block
                                                       (
                                                           LookUpBlock(sourceCollectionLoopingVar, newElement, referenceTracker, mapper),
                                                           Expression.Call(targetCollection, targetCollectionInsertionMethod, newElement)
                                                       )
                                                   )
                                               )
                                               )));
        }