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?))); }
/// <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>))); }
/// <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>))); }
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); }
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<IDataReader, T>.</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); }