// dr --> m internal static Func <IDataReader, object> GetTypeDeserializerImpl(Type mType, IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false) { var dm = new DynamicMethod("Deserialize" + Guid.NewGuid().ToString(), mType, new[] { typeof(IDataReader) }, mType, true); var il = dm.GetILGenerator(); il.DeclareLocal(typeof(int)); il.DeclareLocal(mType); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc_0); if (length == -1) { length = reader.FieldCount - startBound; } var names = Enumerable.Range(startBound, length).Select(i => reader.GetName(i)).ToArray(); ITypeMap typeMap = GetTypeMap(mType); int index = startBound; ConstructorInfo specializedConstructor = null; bool supportInitialize = false; Dictionary <Type, LocalBuilder> structLocals = null; var types = new Type[length]; for (int i = startBound; i < startBound + length; i++) { types[i - startBound] = reader.GetFieldType(i); } var ctor = typeMap.FindConstructor(names, types); if (ctor == null) { string proposedTypes = "(" + string.Join(", ", types.Select((t, i) => t.FullName + " " + names[i]).ToArray()) + ")"; throw new InvalidOperationException($"A parameterless default constructor or one matching signature {proposedTypes} is required for {mType.FullName} materialization"); } if (ctor.GetParameters().Length == 0) { il.Emit(OpCodes.Newobj, ctor); il.Emit(OpCodes.Stloc_1); supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(mType); if (supportInitialize) { il.Emit(OpCodes.Ldloc_1); il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.BeginInit)), null); } } else { specializedConstructor = ctor; } //} il.BeginExceptionBlock(); if (specializedConstructor == null) { il.Emit(OpCodes.Ldloc_1);// [target] } var members = (specializedConstructor != null ? names.Select(n => typeMap.GetConstructorParameter(specializedConstructor, n)) : names.Select(n => typeMap.GetMember(n))).ToList(); // stack is now [target] bool first = true; var allDone = il.DefineLabel(); int enumDeclareLocal = -1, valueCopyLocal = il.DeclareLocal(typeof(object)).LocalIndex; bool applyNullSetting = Settings.ApplyNullValues; foreach (var item in members) { if (item != null) { if (specializedConstructor == null) { il.Emit(OpCodes.Dup); // stack is now [target][target] } Label isDbNullLabel = il.DefineLabel(); Label finishLabel = il.DefineLabel(); il.Emit(OpCodes.Ldarg_0); // stack is now [target][target][reader] EmitInt32(il, index); // stack is now [target][target][reader][index] il.Emit(OpCodes.Dup); // stack is now [target][target][reader][index][index] il.Emit(OpCodes.Stloc_0); // stack is now [target][target][reader][index] il.Emit(OpCodes.Callvirt, Settings.GetItem); // stack is now [target][target][value-as-object] il.Emit(OpCodes.Dup); // stack is now [target][target][value-as-object][value-as-object] StoreLocal(il, valueCopyLocal); Type colType = reader.GetFieldType(index); Type memberType = item.MemberType; if (memberType == typeof(char) || memberType == typeof(char?)) { il.EmitCall(OpCodes.Call, typeof(SqlHelper).GetMethod( memberType == typeof(char) ? nameof(SqlHelper.ReadChar) : nameof(SqlHelper.ReadNullableChar), BindingFlags.Static | BindingFlags.Public), null); // stack is now [target][target][typed-value] } else { il.Emit(OpCodes.Dup); // stack is now [target][target][value][value] il.Emit(OpCodes.Isinst, typeof(DBNull)); // stack is now [target][target][value-as-object][DBNull or null] il.Emit(OpCodes.Brtrue_S, isDbNullLabel); // stack is now [target][target][value-as-object] // unbox nullable enums as the primitive, i.e. byte etc var nullUnderlyingType = Nullable.GetUnderlyingType(memberType); var unboxType = nullUnderlyingType?.IsEnum == true ? nullUnderlyingType : memberType; if (unboxType.IsEnum) { Type numericType = Enum.GetUnderlyingType(unboxType); if (colType == typeof(string)) { if (enumDeclareLocal == -1) { enumDeclareLocal = il.DeclareLocal(typeof(string)).LocalIndex; } il.Emit(OpCodes.Castclass, typeof(string)); // stack is now [target][target][string] StoreLocal(il, enumDeclareLocal); // stack is now [target][target] il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [target][target][enum-type-token] il.EmitCall(OpCodes.Call, typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle)), null); // stack is now [target][target][enum-type] LoadLocal(il, enumDeclareLocal); // stack is now [target][target][enum-type][string] il.Emit(OpCodes.Ldc_I4_1); // stack is now [target][target][enum-type][string][true] il.EmitCall(OpCodes.Call, Settings.EnumParse, null); // stack is now [target][target][enum-as-object] il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value] } else { FlexibleConvertBoxedFromHeadOfStack(il, colType, unboxType, numericType); } if (nullUnderlyingType != null) { il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][typed-value] } } else { TypeCode dataTypeCode = Type.GetTypeCode(colType); TypeCode unboxTypeCode = Type.GetTypeCode(unboxType); if (colType == unboxType || dataTypeCode == unboxTypeCode || dataTypeCode == Type.GetTypeCode(nullUnderlyingType)) { il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value] //} } else { // not a direct match; need to tweak the unbox FlexibleConvertBoxedFromHeadOfStack(il, colType, nullUnderlyingType ?? unboxType, null); if (nullUnderlyingType != null) { il.Emit(OpCodes.Newobj, unboxType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][typed-value] } } } } if (specializedConstructor == null) { // Store the value in the property/field if (item.Property != null) { il.Emit(OpCodes.Callvirt, DefaultTypeMap.GetPropertySetter(item.Property, mType)); } else { il.Emit(OpCodes.Stfld, item.Field); // stack is now [target] } } il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target] il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value] if (specializedConstructor != null) { il.Emit(OpCodes.Pop); if (item.MemberType.IsValueType) { int localIndex = il.DeclareLocal(item.MemberType).LocalIndex; LoadLocalAddress(il, localIndex); il.Emit(OpCodes.Initobj, item.MemberType); LoadLocal(il, localIndex); } else { il.Emit(OpCodes.Ldnull); } } else if (applyNullSetting && (!memberType.IsValueType || Nullable.GetUnderlyingType(memberType) != null)) { il.Emit(OpCodes.Pop); // stack is now [target][target] // can load a null with this value if (memberType.IsValueType) { // must be Nullable<T> for some T GetTempLocal(il, ref structLocals, memberType, true); // stack is now [target][target][null] } else { // regular reference-type il.Emit(OpCodes.Ldnull); // stack is now [target][target][null] } // Store the value in the property/field if (item.Property != null) { il.Emit(OpCodes.Callvirt, DefaultTypeMap.GetPropertySetter(item.Property, mType)); // stack is now [target] } else { il.Emit(OpCodes.Stfld, item.Field); // stack is now [target] } } else { il.Emit(OpCodes.Pop); // stack is now [target][target] il.Emit(OpCodes.Pop); // stack is now [target] } if (first && returnNullIfFirstMissing) { il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldnull); // stack is now [null] il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Br, allDone); } il.MarkLabel(finishLabel); } first = false; index++; } if (specializedConstructor != null) { il.Emit(OpCodes.Newobj, specializedConstructor); } il.Emit(OpCodes.Stloc_1); // stack is empty if (supportInitialize) { il.Emit(OpCodes.Ldloc_1); il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.EndInit)), null); } //} il.MarkLabel(allDone); il.BeginCatchBlock(typeof(Exception)); // stack is Exception il.Emit(OpCodes.Ldloc_0); // stack is Exception, index il.Emit(OpCodes.Ldarg_0); // stack is Exception, index, reader LoadLocal(il, valueCopyLocal); // stack is Exception, index, reader, value il.EmitCall(OpCodes.Call, typeof(SqlHelper).GetMethod(nameof(SqlHelper.ThrowDataException)), null); il.EndExceptionBlock(); il.Emit(OpCodes.Ldloc_1); // stack is [rval] il.Emit(OpCodes.Ret); var funcType = System.Linq.Expressions.Expression.GetFuncType(typeof(IDataReader), mType); return((Func <IDataReader, object>)dm.CreateDelegate(funcType)); }
private static void GenerateDeserializerFromMap(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing, ILGenerator il) { var currentIndexDiagnosticLocal = il.DeclareLocal(typeof(int)); var returnValueLocal = il.DeclareLocal(type); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc, currentIndexDiagnosticLocal); var names = Enumerable.Range(startBound, length).Select(i => reader.GetName(i)).ToArray(); ITypeMap typeMap = GetTypeMap(type); int index = startBound; ConstructorInfo specializedConstructor = null; bool supportInitialize = false; Dictionary <Type, LocalBuilder> structLocals = null; if (type.IsValueType) { il.Emit(OpCodes.Ldloca, returnValueLocal); il.Emit(OpCodes.Initobj, type); } else { var types = new Type[length]; for (int i = startBound; i < startBound + length; i++) { types[i - startBound] = reader.GetFieldType(i); } var explicitConstr = typeMap.FindExplicitConstructor(); if (explicitConstr != null) { var consPs = explicitConstr.GetParameters(); foreach (var p in consPs) { if (!p.ParameterType.IsValueType) { il.Emit(OpCodes.Ldnull); } else { GetTempLocal(il, ref structLocals, p.ParameterType, true); } } il.Emit(OpCodes.Newobj, explicitConstr); il.Emit(OpCodes.Stloc, returnValueLocal); supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type); if (supportInitialize) { il.Emit(OpCodes.Ldloc, returnValueLocal); il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.BeginInit)), null); } } else { var ctor = typeMap.FindConstructor(names, types); if (ctor == null) { string proposedTypes = "(" + string.Join(", ", types.Select((t, i) => t.FullName + " " + names[i]).ToArray()) + ")"; throw new InvalidOperationException($"A parameterless default constructor or one matching signature {proposedTypes} is required for {type.FullName} materialization"); } if (ctor.GetParameters().Length == 0) { il.Emit(OpCodes.Newobj, ctor); il.Emit(OpCodes.Stloc, returnValueLocal); supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type); if (supportInitialize) { il.Emit(OpCodes.Ldloc, returnValueLocal); il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.BeginInit)), null); } } else { specializedConstructor = ctor; } } } il.BeginExceptionBlock(); if (type.IsValueType) { il.Emit(OpCodes.Ldloca, returnValueLocal); // [target] } else if (specializedConstructor == null) { il.Emit(OpCodes.Ldloc, returnValueLocal); // [target] } var members = (specializedConstructor != null ? names.Select(n => typeMap.GetConstructorParameter(specializedConstructor, n)) : names.Select(n => typeMap.GetMember(n))).ToList(); // stack is now [target] bool first = true; var allDone = il.DefineLabel(); var stringEnumLocal = (LocalBuilder)null; var valueCopyDiagnosticLocal = il.DeclareLocal(typeof(object)); bool applyNullSetting = false;//Settings.ApplyNullValues; foreach (var item in members) { if (item != null) { if (specializedConstructor == null) { il.Emit(OpCodes.Dup); // stack is now [target][target] } Label finishLabel = il.DefineLabel(); Type memberType = item.MemberType; // Save off the current index for access if an exception is thrown EmitInt32(il, index); il.Emit(OpCodes.Stloc, currentIndexDiagnosticLocal); LoadReaderValueOrBranchToDBNullLabel(il, index, ref stringEnumLocal, valueCopyDiagnosticLocal, reader.GetFieldType(index), memberType, out var isDbNullLabel); if (specializedConstructor == null) { // Store the value in the property/field if (item.Property != null) { il.Emit(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, DefaultTypeMap.GetPropertySetter(item.Property, type)); } else { il.Emit(OpCodes.Stfld, item.Field); // stack is now [target] } } il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target] il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value] if (specializedConstructor != null) { il.Emit(OpCodes.Pop); LoadDefaultValue(il, item.MemberType); } else if (applyNullSetting && (!memberType.IsValueType || Nullable.GetUnderlyingType(memberType) != null)) { il.Emit(OpCodes.Pop); // stack is now [target][target] // can load a null with this value if (memberType.IsValueType) { // must be Nullable<T> for some T GetTempLocal(il, ref structLocals, memberType, true); // stack is now [target][target][null] } else { // regular reference-type il.Emit(OpCodes.Ldnull); // stack is now [target][target][null] } // Store the value in the property/field if (item.Property != null) { il.Emit(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, DefaultTypeMap.GetPropertySetter(item.Property, type)); // stack is now [target] } else { il.Emit(OpCodes.Stfld, item.Field); // stack is now [target] } } else { il.Emit(OpCodes.Pop); // stack is now [target][target] il.Emit(OpCodes.Pop); // stack is now [target] } if (first && returnNullIfFirstMissing) { il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldnull); // stack is now [null] il.Emit(OpCodes.Stloc, returnValueLocal); il.Emit(OpCodes.Br, allDone); } il.MarkLabel(finishLabel); } first = false; index++; } if (type.IsValueType) { il.Emit(OpCodes.Pop); } else { if (specializedConstructor != null) { il.Emit(OpCodes.Newobj, specializedConstructor); } il.Emit(OpCodes.Stloc, returnValueLocal); // stack is empty if (supportInitialize) { il.Emit(OpCodes.Ldloc, returnValueLocal); il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod(nameof(ISupportInitialize.EndInit)), null); } } il.MarkLabel(allDone); il.BeginCatchBlock(typeof(Exception)); // stack is Exception il.Emit(OpCodes.Ldloc, currentIndexDiagnosticLocal); // stack is Exception, index il.Emit(OpCodes.Ldarg_0); // stack is Exception, index, reader il.Emit(OpCodes.Ldloc, valueCopyDiagnosticLocal); // stack is Exception, index, reader, value il.EmitCall(OpCodes.Call, typeof(MapperHelper).GetMethod(nameof(MapperHelper.ThrowDataException)), null); il.EndExceptionBlock(); il.Emit(OpCodes.Ldloc, returnValueLocal); // stack is [rval] if (type.IsValueType) { il.Emit(OpCodes.Box, type); } il.Emit(OpCodes.Ret); }