/// <summary>
        /// Creates a converter from output parameters to an object of a given type.
        /// </summary>
        /// <param name="command">The command to analyze for the results.</param>
        /// <param name="type">The type to put the values into.</param>
        /// <returns>The converter method.</returns>
        private static Action <IDbCommand, object> CreateClassOutputParameterConverter(IDbCommand command, Type type)
        {
            // get the parameters
            List <IDataParameter> parameters = command.Parameters.Cast <IDataParameter>().ToList();

            // if there are no output parameters, then return an empty method
            if (!parameters.Cast <IDataParameter>().Any(p => p.Direction.HasFlag(ParameterDirection.Output)))
            {
                return (IDbCommand c, object o) => { }
            }
            ;

            // create a dynamic method
            Type typeOwner = type.HasElementType ? type.GetElementType() : type;

            // start creating a dynamic method
            var dm = new DynamicMethod(String.Format(CultureInfo.InvariantCulture, "CreateOutputParameters-{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, typeOwner, true);
            var il = dm.GetILGenerator();

            var localParameters = il.DeclareLocal(typeof(IDataParameterCollection));

            // get the parameters collection from the command into loc.0
            il.Emit(OpCodes.Ldarg_0);                                                                           // push arg.0 (command), stack => [command]
            il.Emit(OpCodes.Callvirt, _iDbCommandGetParameters);                                                // call getparams, stack => [parameters]
            il.Emit(OpCodes.Stloc, localParameters);

            // go through all of the mappings
            var mappings = ColumnMapping.Parameters.CreateMapping(type, null, command, parameters, null, 0, parameters.Count, true);

            for (int i = 0; i < mappings.Length; i++)
            {
                // if there is no parameter for this property, then skip it
                var mapping = mappings[i];
                if (mapping == null)
                {
                    continue;
                }

                // if the property is readonly, then skip it
                var prop = mapping.ClassPropInfo;
                if (!prop.CanSetMember)
                {
                    continue;
                }

                // if the parameter is not output, then skip it
                IDataParameter parameter = parameters[i];
                if (parameter == null || !parameter.Direction.HasFlag(ParameterDirection.Output))
                {
                    continue;
                }

                // push the object on the stack. we will need it to set the value below
                il.Emit(OpCodes.Ldarg_1);

                // get the parameter out of the collection
                il.Emit(OpCodes.Ldloc, localParameters);
                il.Emit(OpCodes.Ldstr, parameter.ParameterName);                                 // push (parametername)
                il.Emit(OpCodes.Callvirt, _iDataParameterCollectionGetItem);

                // get the value out of the parameter
                il.Emit(OpCodes.Callvirt, _iDataParameterGetValue);

                // emit the code to convert the value and set it on the object
                Label finishLabel = TypeConverterGenerator.EmitConvertAndSetValue(il, _dbTypeToTypeMap[parameter.DbType], mapping);
                il.MarkLabel(finishLabel);
            }

            il.Emit(OpCodes.Ret);

            return((Action <IDbCommand, object>)dm.CreateDelegate(typeof(Action <IDbCommand, object>)));
        }
        /// <summary>
        /// Compiles and returns a method that deserializes class type from the subset of fields of an IDataReader record.
        /// </summary>
        /// <param name="type">The type of object to deserialize.</param>
        /// <param name="reader">The reader to analyze.</param>
        /// <param name="structure">The structure of the record being read.</param>
        /// <param name="startColumn">The index of the first column to read.</param>
        /// <param name="columnCount">The number of columns to read.</param>
        /// <param name="createNewObject">True if the method should create a new instance of an object, false to have the object passed in as a parameter.</param>
        /// <param name="isRootObject">True if this object is the root object and should always be created.</param>
        /// <returns>If createNewObject=true, then Func&lt;IDataReader, T&gt;.</returns>
        /// <remarks>This returns a DynamicMethod so that the graph deserializer can call the methods using IL. IL cannot call the dm after it is converted to a delegate.</remarks>
        private static DynamicMethod CreateClassDeserializerDynamicMethod(Type type, IDataReader reader, IRecordStructure structure, int startColumn, int columnCount, bool createNewObject, bool isRootObject)
        {
            // if there are no columns detected for the class, then the deserializer is null
            if (columnCount == 0 && !isRootObject)
            {
                return(null);
            }

            // get the mapping from the reader to the type
            var mapping = ColumnMapping.Tables.CreateMapping(type, reader, null, null, structure, startColumn, columnCount, true);

            // need to know the constructor for the object (except for structs)
            bool            isStruct    = type.IsValueType;
            ConstructorInfo constructor = null;

            if (!isStruct)
            {
                constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
                if (constructor == null)
                {
                    throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot find a default constructor for type {0}", type.FullName));
                }
            }

            // the method can either be:
            // createNewObject => Func<IDataReader, T>
            // !createNewObject => Func<IDataReader, T, T>
            // create a new anonymous method that takes an IDataReader and returns the given type
            var dm = new DynamicMethod(
                String.Format(CultureInfo.InvariantCulture, "Deserialize-{0}-{1}", type.FullName, Guid.NewGuid()),
                type,
                createNewObject ? new[] { typeof(IDataReader) } : new[] { typeof(IDataReader), type },
                true);

            // get the il generator and put some local variables on the stack
            var il                  = dm.GetILGenerator();
            var localIndex          = il.DeclareLocal(typeof(int));
            var localResult         = il.DeclareLocal(type);
            var localValue          = il.DeclareLocal(typeof(object));
            var localIsNotAllDbNull = il.DeclareLocal(typeof(bool));

            // initialize index = 0
            il.Emit(OpCodes.Ldc_I4_0);
            il.Emit(OpCodes.Stloc, localIndex);

            // emit a call to the constructor of the object
            il.BeginExceptionBlock();

            // if we are supposed to create a new object, then new that up, otherwise use the object passed in as an argument
            // this block sets loc.1 with the return value
            if (isStruct)
            {
                if (createNewObject)
                {
                    il.Emit(OpCodes.Ldloca_S, localResult);                     // load the pointer to the result on the stack
                    il.Emit(OpCodes.Initobj, type);                             // initialize the object on the stack
                }
                else
                {
                    il.Emit(OpCodes.Ldarg_1);                                                   // store arg.1 => loc.1
                    il.Emit(OpCodes.Stloc, localResult);
                }
            }
            else
            {
                if (createNewObject)
                {
                    il.Emit(OpCodes.Newobj, constructor);                       // push new T, stack => [target]
                }
                else
                {
                    il.Emit(OpCodes.Ldarg_1);                                   // push arg.1 (T), stack => [target]
                }
                il.Emit(OpCodes.Stloc, localResult);                            // pop loc.1 (result), stack => [empty]
            }

            var returnLabel = il.DefineLabel();

            for (int index = 0; index < columnCount; index++)
            {
                // if there is no matching property for this column, then continue
                if (mapping[index] == null)
                {
                    continue;
                }

                var method = mapping[index].ClassPropInfo;
                if (!method.CanSetMember)
                {
                    continue;
                }

                // store the value as a local variable in case type conversion fails
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Stloc, localValue);

                // load the address of the object we are working on
                if (isStruct)
                {
                    il.Emit(OpCodes.Ldloca_S, localResult);                                     // push pointer to object
                }
                else
                {
                    il.Emit(OpCodes.Ldloc, localResult);                                        // push loc.1 (target), stack => [target]
                }
                // need to call IDataReader.GetItem to get the value of the field
                il.Emit(OpCodes.Ldarg_0);                                               // push arg.0 (reader), stack => [target][reader]
                IlHelper.EmitLdInt32(il, index + startColumn);                          // push index, stack => [target][reader][index]
                // before we call it, put the current index into the index local variable
                il.Emit(OpCodes.Dup);                                                   // dup index, stack => [target][reader][index][index]
                il.Emit(OpCodes.Stloc, localIndex);                                     // pop loc.0 (index), stack => [target][reader][index]
                // now call it
                il.Emit(OpCodes.Callvirt, _iDataReaderGetItem);                         // call getItem, stack => [target][value-as-object]
                // store the value as a local variable in case type conversion fails
                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Stloc, localValue);

                /////////////////////////////////////////////////////////////////////
                // if this a subobject, then check to see if the value is null
                /////////////////////////////////////////////////////////////////////
                if (startColumn > 0)
                {
                    var afterNullCheck = il.DefineLabel();
                    il.Emit(OpCodes.Dup);
                    il.Emit(OpCodes.Ldsfld, typeof(DBNull).GetField("Value"));
                    il.Emit(OpCodes.Ceq);
                    il.Emit(OpCodes.Brtrue, afterNullCheck);
                    il.Emit(OpCodes.Ldc_I4_1);
                    il.Emit(OpCodes.Stloc, localIsNotAllDbNull);
                    il.MarkLabel(afterNullCheck);
                }

                // determine the type of the object in the recordset
                Type sourceType = reader.GetFieldType(index + startColumn);

                // emit the code to convert the value and set the value on the field
                Label finishLabel = TypeConverterGenerator.EmitConvertAndSetValue(il, sourceType, mapping[index]);

                /////////////////////////////////////////////////////////////////////
                // stack should be [target] and ready for the next column
                /////////////////////////////////////////////////////////////////////
                il.MarkLabel(finishLabel);
            }

            /////////////////////////////////////////////////////////////////////
            // if this was a subobject and all of the values are null, then load the default for the object
            /////////////////////////////////////////////////////////////////////
            if (startColumn > 0)
            {
                var afterNullExit = il.DefineLabel();
                il.Emit(OpCodes.Ldloc, localIsNotAllDbNull);
                il.Emit(OpCodes.Brtrue, afterNullExit);
                TypeHelper.EmitDefaultValue(il, type);                                          // load the default for the type
                il.Emit(OpCodes.Stloc, localResult);                                            // store null => loc.1 (target)
                il.Emit(OpCodes.Br, returnLabel);                                               // exit the loop
                il.MarkLabel(afterNullExit);
            }

            il.MarkLabel(returnLabel);

            /////////////////////////////////////////////////////////////////////
            // catch exceptions and rethrow
            /////////////////////////////////////////////////////////////////////
            il.BeginCatchBlock(typeof(Exception));                                                      // stack => [Exception]
            il.Emit(OpCodes.Ldloc, localIndex);                                                         // push loc.0, stack => [Exception][index]
            il.Emit(OpCodes.Ldarg_0);                                                                   // push arg.0, stack => [Exception][index][reader]
            il.Emit(OpCodes.Ldloc, localValue);                                                         // push loc.3, stack => [Exception][index][reader][value]
            il.Emit(OpCodes.Call, TypeConverterGenerator.CreateDataExceptionMethod);
            il.Emit(OpCodes.Throw);                                                                     // stack => DataException
            il.EndExceptionBlock();

            /////////////////////////////////////////////////////////////////////
            // load the return value from the local variable
            /////////////////////////////////////////////////////////////////////
            il.Emit(OpCodes.Ldloc, localResult);                                                        // ld loc.1 (target), stack => [target]
            il.Emit(OpCodes.Ret);

            // create the function
            return(dm);
        }