Ejemplo n.º 1
0
        static DbReaderDeserializer()
        {
            // pre-initialize all of the simple serializers for known types so they can be quickly returned.
            _simpleDeserializers.TryAdd(typeof(object), GetDynamicDeserializer <FastExpando, FastExpando>());
            _simpleDeserializers.TryAdd(typeof(FastExpando), GetDynamicDeserializer <FastExpando, FastExpando>());
            _simpleDeserializers.TryAdd(typeof(ExpandoObject), GetDynamicDeserializer <ExpandoObject, ExpandoObject>());
            _simpleDeserializers.TryAdd(typeof(XmlDocument), GetXmlDocumentDeserializer());
            _simpleDeserializers.TryAdd(typeof(XDocument), GetXDocumentDeserializer());
            _simpleDeserializers.TryAdd(typeof(byte[]), GetByteArrayDeserializer());
            _simpleDeserializers.TryAdd(typeof(char), new Func <IDataReader, char>(r => TypeConverterGenerator.ReadChar(r.GetValue(0))));
            _simpleDeserializers.TryAdd(typeof(char?), new Func <IDataReader, char?>(r => TypeConverterGenerator.ReadNullableChar(r.GetValue(0))));
            _simpleDeserializers.TryAdd(typeof(string), GetValueDeserializer <string>());
            _simpleDeserializers.TryAdd(typeof(Guid), GetGuidDeserializer());
            _simpleDeserializers.TryAdd(typeof(Guid?), GetNullableGuidDeserializer());

            _simpleDeserializers.TryAdd(typeof(byte), GetValueDeserializer <byte>());
            _simpleDeserializers.TryAdd(typeof(short), GetValueDeserializer <short>());
            _simpleDeserializers.TryAdd(typeof(int), GetValueDeserializer <int>());
            _simpleDeserializers.TryAdd(typeof(long), GetValueDeserializer <long>());
            _simpleDeserializers.TryAdd(typeof(decimal), GetValueDeserializer <decimal>());
            _simpleDeserializers.TryAdd(typeof(float), GetValueDeserializer <float>());
            _simpleDeserializers.TryAdd(typeof(double), GetValueDeserializer <double>());
            _simpleDeserializers.TryAdd(typeof(DateTime), GetValueDeserializer <DateTime>());
            _simpleDeserializers.TryAdd(typeof(DateTimeOffset), GetValueDeserializer <DateTimeOffset>());
            _simpleDeserializers.TryAdd(typeof(TimeSpan), GetValueDeserializer(typeof(TimeSpan)));

            _simpleDeserializers.TryAdd(typeof(byte?), GetValueDeserializer <byte?>());
            _simpleDeserializers.TryAdd(typeof(short?), GetValueDeserializer <short?>());
            _simpleDeserializers.TryAdd(typeof(int?), GetValueDeserializer <int?>());
            _simpleDeserializers.TryAdd(typeof(long?), GetValueDeserializer <long?>());
            _simpleDeserializers.TryAdd(typeof(decimal?), GetValueDeserializer <decimal?>());
            _simpleDeserializers.TryAdd(typeof(float?), GetValueDeserializer <float?>());
            _simpleDeserializers.TryAdd(typeof(double?), GetValueDeserializer <double?>());
            _simpleDeserializers.TryAdd(typeof(DateTime?), GetValueDeserializer <DateTime?>());
            _simpleDeserializers.TryAdd(typeof(DateTimeOffset?), GetValueDeserializer <DateTimeOffset?>());
            _simpleDeserializers.TryAdd(typeof(TimeSpan?), GetValueDeserializer(typeof(TimeSpan?)));
        }
Ejemplo n.º 2
0
        /// <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.MapParameters(type, command, parameters);

            for (int i = 0; i < mappings.Count; i++)
            {
                var finishLabel = il.DefineLabel();

                // 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.Member;
                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);

                // if this is a deep mapping, then get the parent object, and do a null test if its not a value type
                if (mapping.IsDeep)
                {
                    ClassPropInfo.EmitGetValue(type, mapping.Prefix, il);

                    if (!ClassPropInfo.FindMember(type, mapping.Prefix).MemberType.IsValueType)
                    {
                        il.Emit(OpCodes.Dup);
                        var label = il.DefineLabel();
                        il.Emit(OpCodes.Brtrue, label);
                        il.Emit(OpCodes.Pop);               // pop the object before finishing
                        il.Emit(OpCodes.Br, finishLabel);
                        il.MarkLabel(label);
                    }
                }

                // 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
                TypeConverterGenerator.EmitConvertAndSetValue(il, _dbTypeToTypeMap[parameter.DbType], mapping);
                il.MarkLabel(finishLabel);
            }

            il.Emit(OpCodes.Ret);

            return((Action <IDbCommand, object>)dm.CreateDelegate(typeof(Action <IDbCommand, object>)));
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Generates an accessor for a property in the object.
        /// </summary>
        /// <param name="type">The type of object being read.</param>
        /// <param name="mapping">The column mapping.</param>
        /// <param name="columnInfo">The column information.</param>
        /// <returns>An accessor function or null if there is no mapping.</returns>
        private static Func <object, object> GenerateAccessor(Type type, FieldMapping mapping, ColumnInfo columnInfo)
        {
            // create a new anonymous method that takes an object and returns the value
            var dm = new DynamicMethod(string.Format(CultureInfo.InvariantCulture, "GetValue-{0}-{1}", type.FullName, Guid.NewGuid()), typeof(object), new[] { typeof(object) }, true);
            var il = dm.GetILGenerator();

            // convert the object reference to the desired type
            il.Emit(OpCodes.Ldarg_0);

            var readyToSetLabel = il.DefineLabel();

            // get the value from the object
            Type sourceType;

            if (mapping != null)
            {
                if (type.GetTypeInfo().IsValueType)
                {
                    // access the field/property of a value type
                    var valueHolder = il.DeclareLocal(type);
                    il.Emit(OpCodes.Unbox_Any, type);
                    il.Emit(OpCodes.Stloc, valueHolder);
                    il.Emit(OpCodes.Ldloca_S, valueHolder);
                }
                else
                {
                    il.Emit(OpCodes.Isinst, type);                                      // cast object -> type
                }

                ClassPropInfo.EmitGetValue(type, mapping.PathToMember, il, readyToSetLabel);
                if (mapping.Member.MemberType.GetTypeInfo().IsValueType)
                {
                    il.Emit(OpCodes.Unbox_Any, mapping.Member.MemberType);
                }

                ClassPropInfo propInfo = mapping.Member;
                sourceType = propInfo.MemberType;

                // if the type is nullable, handle nulls
                Type underlyingType = Nullable.GetUnderlyingType(sourceType);
                if (underlyingType != null)
                {
                    // check for not null
                    Label notNullLabel = il.DefineLabel();

                    var nullableHolder = il.DeclareLocal(propInfo.MemberType);
                    il.Emit(OpCodes.Stloc, nullableHolder);
                    il.Emit(OpCodes.Ldloca_S, nullableHolder);
                    il.Emit(OpCodes.Call, sourceType.GetProperty("HasValue").GetGetMethod());
                    il.Emit(OpCodes.Brtrue_S, notNullLabel);

                    // it's null, just return null
                    il.Emit(OpCodes.Ldnull);
                    il.Emit(OpCodes.Ret);

                    il.MarkLabel(notNullLabel);

                    // it's not null, so unbox to the underlyingtype
                    il.Emit(OpCodes.Ldloca, nullableHolder);
                    il.Emit(OpCodes.Call, sourceType.GetProperty("Value").GetGetMethod());

                    // at this point we have de-nulled value, so use those converters
                    sourceType = underlyingType;
                }
            }
            else
            {
                // there is no mapping, so we're going to convert the object itself to the target type
                sourceType = type;

                // if the value is null then exit immediately
                var notNullLabel = il.DefineLabel();
                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Brfalse, readyToSetLabel);
                il.MarkLabel(notNullLabel);

                // we get it as an object. unbox it so we can apply conversion operations
                if (sourceType.GetTypeInfo().IsValueType)
                {
                    il.Emit(OpCodes.Unbox_Any, sourceType);
                }
            }

            // if the serializer isn't the default ToStringSerializer, and it can serialize the type properly, then use it
            // otherwise we'll use coersion or conversion
            Type targetType   = columnInfo.DataType;
            var  targetDbType = DbParameterGenerator.LookupDbType(targetType);
            bool canSerialize = sourceType != typeof(string) &&
                                mapping != null &&
                                mapping.Serializer.GetType() != typeof(ToStringObjectSerializer) &&
                                mapping.Serializer.CanSerialize(sourceType, targetDbType);

            // attempt to convert the value
            var currentType = sourceType;

            if (currentType != targetType)
            {
                if (canSerialize)
                {
                    if (sourceType.GetTypeInfo().IsValueType)
                    {
                        il.Emit(OpCodes.Box, sourceType);
                    }
                    il.EmitLoadType(sourceType);
                    StaticFieldStorage.EmitLoad(il, mapping.Serializer);
                    il.Emit(OpCodes.Call, typeof(ObjectReader).GetMethod("SerializeObject", BindingFlags.NonPublic | BindingFlags.Static));

                    currentType = typeof(Object);
                }
                else if (TypeConverterGenerator.EmitConversionOrCoersion(il, sourceType, targetType))
                {
                    currentType = targetType;
                }
            }

            // either way, we are putting it in an object variable, so box it
            if (currentType.GetTypeInfo().IsValueType)
            {
                il.Emit(OpCodes.Box, currentType);
            }

            il.MarkLabel(readyToSetLabel);
            il.Emit(OpCodes.Ret);

            return((Func <object, object>)dm.CreateDelegate(typeof(Func <object, object>)));
        }
Ejemplo n.º 4
0
        private static DynamicMethod CreateClassDeserializerDynamicMethod(Type type, IDataReader reader, IRecordStructure structure, int startColumn, int columnCount, bool createNewObject, bool isRootObject, bool allowBindChild, bool checkForAllDbNull)
        {
            // if there are no columns detected for the class, then don't deserialize it
            // exception: a parentandchild object in the middle of a child hierarchy
            var genericType = type.GetTypeInfo().IsGenericType ? type.GetGenericTypeDefinition() : null;

            if (columnCount == 0 && !isRootObject && genericType != typeof(ParentAndChild <,>))
            {
                return(null);
            }

            var mappings = MapColumns(type, reader, startColumn, columnCount, structure, allowBindChild && isRootObject);

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

            // if there is a constructor, determine which columns will be mapped to constructor parameters
            // constructorMappings will be indexes into the mappin array (or -1 if there is no mapping)
            var constructorParameters = (constructor != null) ? constructor.GetParameters() : null;
            var constructorMappings   = (constructor != null) ? constructorParameters.Select(p => mappings.FindIndex(m => m != null && p.Name.IsIEqualTo(m.Member.Name))).ToArray() : new int[0];

            // remove any mappings that are not settable and not used for constructor initialization
            // this will save on translating properties we can't set and prevent issue #459
            for (var m = 0; m < mappings.Count; m++)
            {
                if (mappings[m] != null && !mappings[m].Member.CanSetMember && !constructorMappings.Contains(m))
                {
                    mappings[m] = null;
                }
            }

            // 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));

            // initialization
            il.Emit(OpCodes.Ldc_I4_0);
            il.Emit(OpCodes.Stloc, localIndex);

            /////////////////////////////////////////////////////////////////////
            // read all of the values into local variables
            /////////////////////////////////////////////////////////////////////
            il.BeginExceptionBlock();
            var hasAtLeastOneMapping = false;
            var localValues          = new LocalBuilder[mappings.Count];

            for (int index = 0; index < columnCount; index++)
            {
                var mapping = mappings[index];
                if (mapping == null)
                {
                    continue;
                }
                hasAtLeastOneMapping = true;

                var member = mapping.Member;

                localValues[index] = il.DeclareLocal(member.MemberType);

                // need to call IDataReader.GetItem to get the value of the field
                il.Emit(OpCodes.Ldarg_0);
                IlHelper.EmitLdInt32(il, index + startColumn);

                // before we call it, put the current index into the index local variable
                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Stloc, localIndex);

                // now call it
                il.Emit(OpCodes.Callvirt, _iDataReaderGetItem);

                // if handling a subobject, we 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);
                }

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

                // convert the value and store it locally
                Type sourceType = reader.GetFieldType(index + startColumn);
                TypeConverterGenerator.EmitConvertValue(il, member.Name, sourceType, member.MemberType, mapping.Serializer);
                il.Emit(OpCodes.Stloc, localValues[index]);
            }

            /////////////////////////////////////////////////////////////////////
            // catch translation 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();

            /////////////////////////////////////////////////////////////////////
            // if this was a subobject and all of the values are null, then return the default for the object
            /////////////////////////////////////////////////////////////////////
            if (hasAtLeastOneMapping && checkForAllDbNull)
            {
                var afterNullExit = il.DefineLabel();
                il.Emit(OpCodes.Ldloc, localIsNotAllDbNull);
                il.Emit(OpCodes.Brtrue, afterNullExit);
                TypeHelper.EmitDefaultValue(il, type);
                il.Emit(OpCodes.Ret);
                il.MarkLabel(afterNullExit);
            }

            /////////////////////////////////////////////////////////////////////
            // call the constructor
            /////////////////////////////////////////////////////////////////////
            if (createNewObject)
            {
                if (isStruct)
                {
                    il.Emit(OpCodes.Ldloca_S, localResult);
                    il.Emit(OpCodes.Initobj, type);
                    if (constructor != null)
                    {
                        il.Emit(OpCodes.Ldloca_S, localResult);
                    }
                }

                // if there is a constructor, then populate the values from the pre-converted local values we created above
                if (constructor != null)
                {
                    for (var p = 0; p < constructorParameters.Length; p++)
                    {
                        if (constructorMappings[p] >= 0)
                        {
                            il.Emit(OpCodes.Ldloc, localValues[constructorMappings[p]]);
                        }
                        else
                        {
                            TypeHelper.EmitDefaultValue(il, constructorParameters[p].ParameterType);
                        }
                    }
                }

                if (isStruct)
                {
                    if (constructor != null)
                    {
                        il.Emit(OpCodes.Call, constructor);
                    }
                }
                else
                {
                    il.Emit(OpCodes.Newobj, constructor);
                    il.Emit(OpCodes.Stloc, localResult);
                }
            }
            else
            {
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(OpCodes.Stloc, localResult);
            }

            /////////////////////////////////////////////////////////////////////
            // for anything not passed to the constructor, copy the local values to the properties
            /////////////////////////////////////////////////////////////////////
            for (int index = 0; index < columnCount; index++)
            {
                var mapping = mappings[index];
                if (mapping == null)
                {
                    continue;
                }

                // don't attempt to set values that can't be set at all
                var member = mapping.Member;
                if (!member.CanSetMember)
                {
                    continue;
                }

                // don't set values that have already been set in the constructor
                if (constructorMappings.Contains(index))
                {
                    continue;
                }

                // load the address of the object we are working on
                if (isStruct)
                {
                    il.Emit(OpCodes.Ldloca_S, localResult);
                }
                else
                {
                    il.Emit(OpCodes.Ldloc, localResult);
                }

                // for deep mappings, go to the parent of the field that we are trying to set
                var nextLabel = il.DefineLabel();
                if (mapping.IsDeep)
                {
                    ClassPropInfo.EmitGetValue(type, mapping.Prefix, il);

                    // if the mapping parent is nullable, check to see if it is null.
                    // if so, pop the parent off the stack and move to the next field
                    if (!ClassPropInfo.FindMember(type, mapping.Prefix).MemberType.GetTypeInfo().IsValueType)
                    {
                        var notNullLabel = il.DefineLabel();
                        il.Emit(OpCodes.Dup);
                        il.Emit(OpCodes.Brtrue, notNullLabel);
                        il.Emit(OpCodes.Pop);
                        il.Emit(OpCodes.Br, nextLabel);
                        il.MarkLabel(notNullLabel);
                    }
                }

                // load the value from the local and set it on the object
                il.Emit(OpCodes.Ldloc, localValues[index]);
                member.EmitSetValue(il);
                il.MarkLabel(nextLabel);

                /////////////////////////////////////////////////////////////////////
                // stack should be [target] and ready for the next column
                /////////////////////////////////////////////////////////////////////
            }

            /////////////////////////////////////////////////////////////////////
            // 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);
        }
Ejemplo n.º 5
0
        private ObjectReader(IDbCommand command, Type type, IDataReader reader)
        {
            var provider = InsightDbProvider.For(command);

            // create a mapping, and only keep mappings that match our modified schema
            var mappings = ColumnMapping.MapColumns(type, reader).ToList();

            // copy the schema and fix it
            SchemaTable = reader.GetSchemaTable().Copy();
            FixupSchemaNumericScale();
            FixupSchemaRemoveReadOnlyColumns(mappings);

            IsAtomicType = TypeHelper.IsAtomicType(type);
            if (!IsAtomicType)
            {
                int columnCount = SchemaTable.Rows.Count;
                _accessors   = new Func <object, object> [columnCount];
                _memberTypes = new Type[columnCount];

                for (int i = 0; i < columnCount; i++)
                {
                    var mapping = mappings[i];
                    if (mapping == null)
                    {
                        continue;
                    }

                    ClassPropInfo propInfo = mapping.Member;

                    // create a new anonymous method that takes an object and returns the value
                    var dm = new DynamicMethod(string.Format(CultureInfo.InvariantCulture, "GetValue-{0}-{1}", type.FullName, Guid.NewGuid()), typeof(object), new[] { typeof(object) }, true);
                    var il = dm.GetILGenerator();

                    // convert the object reference to the desired type
                    il.Emit(OpCodes.Ldarg_0);
                    if (type.IsValueType)
                    {
                        // access the field/property of a value type
                        var valueHolder = il.DeclareLocal(type);
                        il.Emit(OpCodes.Unbox_Any, type);
                        il.Emit(OpCodes.Stloc, valueHolder);
                        il.Emit(OpCodes.Ldloca_S, valueHolder);
                    }
                    else
                    {
                        il.Emit(OpCodes.Isinst, type);                                                          // cast object -> type
                    }
                    // get the value from the object
                    var readyToSetLabel = il.DefineLabel();
                    ClassPropInfo.EmitGetValue(type, mapping.PathToMember, il, readyToSetLabel);
                    if (mapping.Member.MemberType.IsValueType)
                    {
                        il.Emit(OpCodes.Unbox_Any, mapping.Member.MemberType);
                    }

                    // if the type is nullable, handle nulls
                    Type sourceType     = propInfo.MemberType;
                    Type targetType     = (Type)SchemaTable.Rows[i]["DataType"];
                    Type underlyingType = Nullable.GetUnderlyingType(sourceType);
                    if (underlyingType != null)
                    {
                        // check for not null
                        Label notNullLabel = il.DefineLabel();

                        var nullableHolder = il.DeclareLocal(propInfo.MemberType);
                        il.Emit(OpCodes.Stloc, nullableHolder);
                        il.Emit(OpCodes.Ldloca_S, nullableHolder);
                        il.Emit(OpCodes.Call, sourceType.GetProperty("HasValue").GetGetMethod());
                        il.Emit(OpCodes.Brtrue_S, notNullLabel);

                        // it's null, just return null
                        il.Emit(OpCodes.Ldnull);
                        il.Emit(OpCodes.Ret);

                        il.MarkLabel(notNullLabel);

                        // it's not null, so unbox to the underlyingtype
                        il.Emit(OpCodes.Ldloca, nullableHolder);
                        il.Emit(OpCodes.Call, sourceType.GetProperty("Value").GetGetMethod());

                        // at this point we have de-nulled value, so use those converters
                        sourceType = underlyingType;
                    }

                    if (sourceType != targetType && !sourceType.IsValueType && sourceType != typeof(string))
                    {
                        il.EmitLoadType(sourceType);
                        StaticFieldStorage.EmitLoad(il, mapping.Serializer);
                        il.Emit(OpCodes.Call, typeof(ObjectReader).GetMethod("SerializeObject", BindingFlags.NonPublic | BindingFlags.Static));
                    }
                    else
                    {
                        // attempt to convert the value
                        // either way, we are putting it in an object variable, so box it
                        if (TypeConverterGenerator.EmitConversionOrCoersion(il, sourceType, targetType))
                        {
                            il.Emit(OpCodes.Box, targetType);
                        }
                        else
                        {
                            il.Emit(OpCodes.Box, sourceType);
                        }
                    }

                    il.MarkLabel(readyToSetLabel);
                    il.Emit(OpCodes.Ret);

                    _memberTypes[i] = propInfo.MemberType;
                    _accessors[i]   = (Func <object, object>)dm.CreateDelegate(typeof(Func <object, object>));
                }
            }
            else
            {
                // we are working off a single-column atomic type
                _memberTypes = new Type[1] {
                    type
                };
                _accessors = new Func <object, object>[] { o => o };
            }
        }
        /// <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);
        }