/// <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>
        protected virtual void CreateConstructorArgumentExpressionsForMapping(ClassMap map, List <Expression> argumentExpressions)
        {
            foreach (var parameterMap in map.ParameterMaps)
            {
                if (parameterMap.ConstructorTypeMap != null)
                {
                    // Constructor paramter type.
                    var arguments = new List <Expression>();
                    CreateConstructorArgumentExpressionsForMapping(parameterMap.ConstructorTypeMap, arguments);
                    var constructorExpression = Expression.New(Reader.Configuration.GetConstructor(parameterMap.ConstructorTypeMap.ClassType), arguments);

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

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

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

                    var index = Reader.Configuration.HasHeaderRecord
                                                ? Reader.GetFieldIndex(parameterMap.Data.Name, 0)
                                                : 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)));

                    // Convert the field.
                    var typeConverterExpression = Expression.Constant(parameterMap.Data.TypeConverter);
                    parameterMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions {
                        CultureInfo = Reader.Context.ReaderConfiguration.CultureInfo
                    }, Reader.Context.ReaderConfiguration.TypeConverterOptionsCache.GetOptions(parameterMap.Data.Parameter.ParameterType), parameterMap.Data.TypeConverterOptions);

                    // Create type converter expression.
                    var memberMapData = new MemberMapData(null)
                    {
                        Index                = parameterMap.Data.Index,
                        TypeConverter        = parameterMap.Data.TypeConverter,
                        TypeConverterOptions = parameterMap.Data.TypeConverterOptions
                    };
                    memberMapData.Names.Add(parameterMap.Data.Name);
                    Expression typeConverterFieldExpression = Expression.Call(typeConverterExpression, nameof(ITypeConverter.ConvertFromString), null, fieldExpression, Expression.Constant(Reader), Expression.Constant(memberMapData));
                    typeConverterFieldExpression = Expression.Convert(typeConverterFieldExpression, parameterMap.Data.Parameter.ParameterType);

                    fieldExpression = typeConverterFieldExpression;

                    argumentExpressions.Add(fieldExpression);
                }
            }
        }
        /// <summary>
        /// Creates the action delegate used to hydrate a record's members with data from the reader.
        /// </summary>
        /// <typeparam name="T">The record type.</typeparam>
        protected virtual Action <T> CreateHydrateRecordAction <T>()
        {
            var recordType = typeof(T);

            if (reader.Context.ReaderConfiguration.Maps[recordType] == null)
            {
                reader.Context.ReaderConfiguration.Maps.Add(reader.Context.ReaderConfiguration.AutoMap(recordType));
            }

            var mapping = reader.Context.ReaderConfiguration.Maps[recordType];

            var recordTypeParameter = Expression.Parameter(recordType, "record");
            var memberAssignments   = new List <Expression>();

            foreach (var memberMap in mapping.MemberMaps)
            {
                var fieldExpression = expressionManager.CreateGetFieldExpression(memberMap);
                if (fieldExpression == null)
                {
                    continue;
                }

                var memberTypeParameter = Expression.Parameter(memberMap.Data.Member.MemberType(), "member");
                var memberAccess        = Expression.MakeMemberAccess(recordTypeParameter, memberMap.Data.Member);
                var memberAssignment    = Expression.Assign(memberAccess, fieldExpression);

                memberAssignments.Add(memberAssignment);
            }

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

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

                var referenceBody = expressionManager.CreateInstanceAndAssignMembers(referenceMap.Data.Member.MemberType(), referenceAssignments);

                var memberTypeParameter = Expression.Parameter(referenceMap.Data.Member.MemberType(), "referenceMember");
                var memberAccess        = Expression.MakeMemberAccess(recordTypeParameter, referenceMap.Data.Member);
                var memberAssignment    = Expression.Assign(memberAccess, referenceBody);
                memberAssignments.Add(memberAssignment);
            }

            var body = Expression.Block(memberAssignments);

            return(Expression.Lambda <Action <T> >(body, recordTypeParameter).Compile());
        }
        /// <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());
        }