Beispiel #1
0
        /// <summary>
        /// Maps a constructor parameter to a CSV field.
        /// </summary>
        /// <param name="name">The name of the constructor parameter.</param>
        public virtual ParameterMap Parameter(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentNullException(nameof(name));
            }

            var args = new GetConstructorArgs(ClassType);

            return(Parameter(() => ConfigurationFunctions.GetConstructor(args), name));
        }
Beispiel #2
0
        /// <summary>
        /// Creates the member assignments for the given <see cref="ClassMap"/>.
        /// </summary>
        /// <param name="mapping">The mapping to create the assignments for.</param>
        /// <param name="assignments">The assignments that will be added to from the mapping.</param>
        public virtual void CreateMemberAssignmentsForMapping(ClassMap mapping, List <MemberAssignment> assignments)
        {
            foreach (var memberMap in mapping.MemberMaps)
            {
                var fieldExpression = CreateGetFieldExpression(memberMap);
                if (fieldExpression == null)
                {
                    continue;
                }

                assignments.Add(Expression.Bind(memberMap.Data.Member, fieldExpression));
            }

            foreach (var referenceMap in mapping.ReferenceMaps)
            {
                if (!reader.CanRead(referenceMap))
                {
                    continue;
                }

                Expression referenceBody;
                if (referenceMap.Data.Mapping.ParameterMaps.Count > 0)
                {
                    var arguments = new List <Expression>();
                    CreateConstructorArgumentExpressionsForMapping(referenceMap.Data.Mapping, arguments);
                    var args = new GetConstructorArgs
                    {
                        ClassType = referenceMap.Data.Mapping.ClassType,
                    };
                    referenceBody = Expression.New(reader.Configuration.GetConstructor(args), arguments);
                }
                else
                {
                    var referenceAssignments = new List <MemberAssignment>();
                    CreateMemberAssignmentsForMapping(referenceMap.Data.Mapping, referenceAssignments);
                    referenceBody = CreateInstanceAndAssignMembers(referenceMap.Data.Member.MemberType(), referenceAssignments);
                }

                assignments.Add(Expression.Bind(referenceMap.Data.Member, referenceBody));
            }
        }
        /// <summary>
        /// Creates a <see cref="Delegate"/> of type <see cref="Func{T}"/>
        /// that will create a record of the given type using the current
        /// reader row.
        /// </summary>
        /// <param name="recordType">The record type.</param>
        protected override Delegate CreateCreateRecordDelegate(Type recordType)
        {
            if (Reader.Context.Maps[recordType] == null)
            {
                Reader.Context.Maps.Add(Reader.Context.AutoMap(recordType));
            }

            var map = Reader.Context.Maps[recordType];

            Expression body;

            if (map.ParameterMaps.Count > 0)
            {
                // This is a constructor parameter type.
                var arguments = new List <Expression>();
                ExpressionManager.CreateConstructorArgumentExpressionsForMapping(map, arguments);

                var args = new GetConstructorArgs
                {
                    ClassType = map.ClassType
                };

                body = Expression.New(Reader.Configuration.GetConstructor(args), arguments);
            }
            else
            {
                var assignments = new List <MemberAssignment>();
                ExpressionManager.CreateMemberAssignmentsForMapping(map, assignments);

                if (assignments.Count == 0)
                {
                    throw new ReaderException(Reader.Context, $"No members are mapped for type '{recordType.FullName}'.");
                }

                body = ExpressionManager.CreateInstanceAndAssignMembers(recordType, assignments);
            }

            var funcType = typeof(Func <>).MakeGenericType(recordType);

            return(Expression.Lambda(funcType, body).Compile());
        }
Beispiel #4
0
 /// <summary>
 /// Returns the type's constructor with the most parameters.
 /// If two constructors have the same number of parameters, then
 /// there is no guarantee which one will be returned. If you have
 /// that situation, you should probably implement this function yourself.
 /// </summary>
 public static ConstructorInfo GetConstructor(GetConstructorArgs args)
 {
     return(args.ClassType.GetConstructorWithMostParameters());
 }
Beispiel #5
0
        /// <summary>
        /// Creates the constructor arguments used to create a type.
        /// </summary>
        /// <param name="map">The mapping to create the arguments for.</param>
        /// <param name="argumentExpressions">The arguments that will be added to the mapping.</param>
        public virtual void CreateConstructorArgumentExpressionsForMapping(ClassMap map, List <Expression> argumentExpressions)
        {
            foreach (var parameterMap in map.ParameterMaps)
            {
                if (parameterMap.Data.IsConstantSet)
                {
                    var constantExpression = Expression.Convert(Expression.Constant(parameterMap.Data.Constant), parameterMap.Data.Parameter.ParameterType);
                    argumentExpressions.Add(constantExpression);

                    continue;
                }

                if (parameterMap.Data.Ignore)
                {
                    Expression defaultExpression;
                    if (parameterMap.Data.IsDefaultSet)
                    {
                        defaultExpression = Expression.Convert(Expression.Constant(parameterMap.Data.Default), parameterMap.Data.Parameter.ParameterType);
                    }
                    else if (parameterMap.Data.Parameter.HasDefaultValue)
                    {
                        defaultExpression = Expression.Convert(Expression.Constant(parameterMap.Data.Parameter.DefaultValue), parameterMap.Data.Parameter.ParameterType);
                    }
                    else
                    {
                        defaultExpression = Expression.Default(parameterMap.Data.Parameter.ParameterType);
                    }

                    argumentExpressions.Add(defaultExpression);

                    continue;
                }

                if (parameterMap.ConstructorTypeMap != null)
                {
                    // Constructor parameter type.
                    var arguments = new List <Expression>();
                    CreateConstructorArgumentExpressionsForMapping(parameterMap.ConstructorTypeMap, arguments);
                    var args = new GetConstructorArgs
                    {
                        ClassType = parameterMap.ConstructorTypeMap.ClassType,
                    };
                    var constructorExpression = Expression.New(reader.Configuration.GetConstructor(args), arguments);

                    argumentExpressions.Add(constructorExpression);
                }
                else if (parameterMap.ReferenceMap != null)
                {
                    // Reference type.

                    var referenceAssignments = new List <MemberAssignment>();
                    CreateMemberAssignmentsForMapping(parameterMap.ReferenceMap.Data.Mapping, referenceAssignments);

                    var referenceBody = CreateInstanceAndAssignMembers(parameterMap.ReferenceMap.Data.Parameter.ParameterType, referenceAssignments);
                    argumentExpressions.Add(referenceBody);
                }
                else
                {
                    // Value type.

                    int index;
                    if (parameterMap.Data.IsNameSet || reader.Configuration.HasHeaderRecord && !parameterMap.Data.IsIndexSet)
                    {
                        // Use name.
                        index = reader.GetFieldIndex(parameterMap.Data.Names.ToArray(), parameterMap.Data.NameIndex, parameterMap.Data.IsOptional);
                        if (index == -1)
                        {
                            if (parameterMap.Data.IsDefaultSet || parameterMap.Data.IsOptional)
                            {
                                var defaultExpression = CreateDefaultExpression(parameterMap, Expression.Constant(string.Empty));
                                argumentExpressions.Add(defaultExpression);
                                continue;
                            }

                            // Skip if the index was not found.
                            continue;
                        }
                    }
                    else if (!parameterMap.Data.IsIndexSet && parameterMap.Data.IsOptional)
                    {
                        // If there wasn't an index explicitly, use a default value since constructors need all
                        // arguments to be created.
                        var defaultExpression = CreateDefaultExpression(parameterMap, Expression.Constant(string.Empty));
                        argumentExpressions.Add(defaultExpression);
                        continue;
                    }
                    else
                    {
                        // Use index.
                        index = parameterMap.Data.Index;
                    }

                    // Get the field using the field index.
                    var        method          = typeof(IReaderRow).GetProperty("Item", typeof(string), new[] { typeof(int) }).GetGetMethod();
                    Expression fieldExpression = Expression.Call(Expression.Constant(reader), method, Expression.Constant(index, typeof(int)));

                    if (parameterMap.Data.IsDefaultSet)
                    {
                        fieldExpression = CreateDefaultExpression(parameterMap, fieldExpression);
                    }
                    else
                    {
                        fieldExpression = CreateTypeConverterExpression(parameterMap, fieldExpression);
                    }

                    argumentExpressions.Add(fieldExpression);
                }
            }
        }
Beispiel #6
0
        /// <summary>
        /// Auto maps the given map using constructor parameters.
        /// </summary>
        /// <param name="map">The map.</param>
        /// <param name="context">The context.</param>
        /// <param name="mapParents">The list of parents for the map.</param>
        /// <param name="indexStart">The index starting point.</param>
        protected virtual void AutoMapConstructorParameters(ClassMap map, CsvContext context, LinkedList <Type> mapParents, int indexStart = 0)
        {
            var type        = map.GetGenericType();
            var args        = new GetConstructorArgs(map.ClassType);
            var constructor = context.Configuration.GetConstructor(args);
            var parameters  = constructor.GetParameters();

            foreach (var parameter in parameters)
            {
                var parameterMap = new ParameterMap(parameter);

                if (parameter.GetCustomAttributes <IgnoreAttribute>(true).Any() || parameter.GetCustomAttributes <ConstantAttribute>(true).Any())
                {
                    // If there is an IgnoreAttribute or ConstantAttribute, we still need to add a map because a constructor requires
                    // all parameters to be present. A default value will be used later on.

                    ApplyAttributes(parameterMap);
                    map.ParameterMaps.Add(parameterMap);
                    continue;
                }

                var typeConverterType  = context.TypeConverterCache.GetConverter(parameter.ParameterType).GetType();
                var memberTypeInfo     = parameter.ParameterType.GetTypeInfo();
                var isDefaultConverter = typeConverterType == typeof(DefaultTypeConverter);
                if (isDefaultConverter && (memberTypeInfo.HasParameterlessConstructor() || memberTypeInfo.IsUserDefinedStruct()))
                {
                    // If the type is not one covered by our type converters
                    // and it has a parameterless constructor, create a
                    // reference map for it.

                    if (context.Configuration.IgnoreReferences)
                    {
                        throw new InvalidOperationException($"Configuration '{nameof(CsvConfiguration.IgnoreReferences)}' can't be true " +
                                                            "when using types without a default constructor. Constructor parameters " +
                                                            "are used and all members including references must be used.");
                    }

                    if (CheckForCircularReference(parameter.ParameterType, mapParents))
                    {
                        throw new InvalidOperationException($"A circular reference was detected in constructor paramter '{parameter.Name}'." +
                                                            "Since all parameters must be supplied for a constructor, this parameter can't be skipped.");
                    }

                    mapParents.AddLast(type);
                    var refMapType = typeof(DefaultClassMap <>).MakeGenericType(parameter.ParameterType);
                    var refMap     = (ClassMap)ObjectResolver.Current.Resolve(refMapType);
                    AutoMapMembers(refMap, context, mapParents, Math.Max(map.GetMaxIndex(isParameter: true) + 1, indexStart));
                    mapParents.Drop(mapParents.Find(type));

                    var referenceMap = new ParameterReferenceMap(parameter, refMap);
                    if (context.Configuration.ReferenceHeaderPrefix != null)
                    {
                        var referenceHeaderPrefix = new ReferenceHeaderPrefixArgs(memberTypeInfo.MemberType(), memberTypeInfo.Name);
                        referenceMap.Data.Prefix = context.Configuration.ReferenceHeaderPrefix(referenceHeaderPrefix);
                    }

                    ApplyAttributes(referenceMap);

                    parameterMap.ReferenceMap = referenceMap;
                }
                else if (context.Configuration.ShouldUseConstructorParameters(new ShouldUseConstructorParametersArgs(parameter.ParameterType)))
                {
                    mapParents.AddLast(type);
                    var constructorMapType = typeof(DefaultClassMap <>).MakeGenericType(parameter.ParameterType);
                    var constructorMap     = (ClassMap)ObjectResolver.Current.Resolve(constructorMapType);
                    // Need to use Max here for nested types.
                    AutoMapConstructorParameters(constructorMap, context, mapParents, Math.Max(map.GetMaxIndex(isParameter: true) + 1, indexStart));
                    mapParents.Drop(mapParents.Find(type));

                    parameterMap.ConstructorTypeMap = constructorMap;
                }
                else
                {
                    parameterMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions(), context.TypeConverterOptionsCache.GetOptions(parameter.ParameterType), parameterMap.Data.TypeConverterOptions);
                    parameterMap.Data.Index = map.GetMaxIndex(isParameter: true) + 1;

                    ApplyAttributes(parameterMap);
                }

                map.ParameterMaps.Add(parameterMap);
            }

            map.ReIndex(indexStart);
        }