/// <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) { if (mapping == null) { return(null); } 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 = columnInfo.ColumnType; 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 the serializer isn't the default ToStringSerializer, and it can serialize the type properly, then use it // otherwise we'll use coersion or conversion var targetDbType = DbParameterGenerator.LookupDbType(targetType); bool canSerialize = sourceType != typeof(string) && mapping.Serializer.GetType() != typeof(ToStringObjectSerializer) && mapping.Serializer.CanSerialize(sourceType, targetDbType); if (sourceType != targetType && canSerialize) { if (sourceType.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)); } 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); return((Func <object, object>)dm.CreateDelegate(typeof(Func <object, object>))); }
private static void GenerateReturnsStructure(MethodInfo interfaceMethod, ILGenerator mIL) { // if we are returning a task, unwrap the task result var returnType = interfaceMethod.ReturnType; if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task <>)) { returnType = returnType.GetGenericArguments()[0]; } // if there are returns attributes specified, then build the structure for the coder var returnsAttributes = interfaceMethod.GetCustomAttributes(true).OfType <RecordsetAttribute>().ToDictionary(r => r.Index); if (returnType.IsSubclassOf(typeof(Results)) && !returnType.Name.StartsWith("Results`", StringComparison.OrdinalIgnoreCase)) { // we have a return that is derived from results mIL.Emit(OpCodes.Ldsfld, typeof(DerivedResultsReader <>).MakeGenericType(returnType).GetField("Default")); } else { bool isSingle = !returnType.IsSubclassOf(typeof(Results)) && !IsGenericListType(returnType); // we are returning results<T...>, or IList<T...> var returnTypeArgs = isSingle ? new Type[] { returnType } : returnType.GetGenericArguments(); Type currentType = null; // go through all of the type arguments or recordsets int returnIndex = 0; for (int i = 0; i < Math.Max(returnTypeArgs.Length, returnsAttributes.Keys.MaxOrDefault(k => k + 1)); i++) { RecordsetAttribute r; returnsAttributes.TryGetValue(i, out r); var types = (r != null) ? r.Types : new Type[] { returnTypeArgs[i] }; // grab the records field for the appropriate OneToOne mapping mIL.Emit(OpCodes.Ldsfld, Query.GetOneToOneType(types).GetField("Records", BindingFlags.Public | BindingFlags.Static)); // keep track of the type that we are returning if (r == null || !r.IsChild) { returnIndex++; } if (i == 0) { // start the chain of calls if (isSingle) { var recordReader = typeof(IRecordReader <>).MakeGenericType(returnType); var readerType = typeof(SingleReader <>).MakeGenericType(returnType); var constructor = readerType.GetConstructor(new Type[] { recordReader }); mIL.Emit(OpCodes.Newobj, constructor); currentType = readerType; } else if (returnType.IsSubclassOf(typeof(Results))) { var method = typeof(Query).GetMethod("ReturnsResults", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(types[0]); currentType = method.ReturnType; mIL.Emit(OpCodes.Call, method); } else { // IList<T> or List<T>, etc... var oneToOneBaseType = typeof(OneToOne <>).MakeGenericType(types[0]); currentType = typeof(ListReader <>).MakeGenericType(types[0]); Type readerType = currentType; // if we're not returning an IList, then we need to insert the reader adapter class if (returnType.GetGenericTypeDefinition() != typeof(IList <>)) { readerType = typeof(ListReaderAdapter <,>).MakeGenericType(returnType, types[0]); } mIL.Emit(OpCodes.Newobj, readerType.GetConstructor(new Type[] { oneToOneBaseType })); } } else if (r != null && r.IsChild) { // the parent is single and we haven't overridden the id field var parentType = currentType.GetGenericArguments()[0]; var childType = types[0]; var list = ChildMapperHelper.GetListSetter(parentType, childType, r.Into); var listMethod = typeof(ClassPropInfo).GetMethod("CreateSetMethod").MakeGenericMethod(parentType, typeof(List <>).MakeGenericType(childType)).Invoke(list, Parameters.EmptyArray); MethodInfo method; if (r.Id == null && TypeIsSingleReader(currentType)) { // previous and recordReader are on the stack, add the id and list method StaticFieldStorage.EmitLoad(mIL, listMethod, _moduleBuilder); method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static) .Single( mi => mi.Name == "ThenChildren" && mi.GetGenericArguments().Length == 2 && currentType.GetGenericTypeDefinition().Name == mi.GetParameters()[0].ParameterType.Name) .MakeGenericMethod( new Type[] { parentType, // TParent childType, // TChild }); } else { var recordReaderType = typeof(RecordReader <>).MakeGenericType(childType); var parentid = ChildMapperHelper.GetIDAccessor(parentType, r.Id); var parentIdType = parentid.MemberType; var parentIdMethod = parentid.GetType().GetMethod("CreateGetMethod").MakeGenericMethod(parentType, parentIdType).Invoke(parentid, Parameters.EmptyArray); // if groupby is specified, then convert the RecordReader to an IChildRecordReader by groupby if (r.GroupBy != null) { var childid = ChildMapperHelper.FindParentIDAccessor(childType, r.GroupBy, parentType); if (childid == null) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot find GroupBy {0} on {1}", r.GroupBy, childType.FullName)); } var getMethod = childid.GetType().GetMethod("CreateGetMethod").MakeGenericMethod(childType, parentIdType).Invoke(childid, Parameters.EmptyArray); StaticFieldStorage.EmitLoad(mIL, getMethod, _moduleBuilder); var groupByMethod = recordReaderType.GetMethod("GroupBy").MakeGenericMethod(parentIdType); mIL.Emit(OpCodes.Call, groupByMethod); recordReaderType = groupByMethod.ReturnType; } // previous and recordReader are on the stack, add the id and list methods StaticFieldStorage.EmitLoad(mIL, parentIdMethod, _moduleBuilder); StaticFieldStorage.EmitLoad(mIL, listMethod, _moduleBuilder); method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static) .Single( mi => mi.Name == "ThenChildren" && mi.GetGenericArguments().Length == 3 && currentType.GetGenericTypeDefinition().Name == mi.GetParameters()[0].ParameterType.Name && mi.GetParameters()[1].ParameterType.Name == recordReaderType.Name && mi.GetParameters().Any(p => String.Compare(p.Name, "id", StringComparison.OrdinalIgnoreCase) == 0)) .MakeGenericMethod( new Type[] { parentType, // TParent childType, // TChild parentIdType // TId }); } mIL.Emit(OpCodes.Call, method); currentType = method.ReturnType; } else { var method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static) .Single(mi => mi.Name == "Then" && mi.GetGenericArguments().Length == returnIndex && mi.GetParameters()[0].ParameterType.Name.StartsWith("IQueryReader", StringComparison.OrdinalIgnoreCase)) .MakeGenericMethod(returnTypeArgs.Take(returnIndex).ToArray()); mIL.Emit(OpCodes.Call, method); currentType = method.ReturnType; } } } }
/// <summary> /// Emits the IL to convert a value to a target type. /// </summary> /// <param name="il">The IL generator to output to.</param> /// <param name="memberName">The name of the member being converted.</param> /// <param name="sourceType">The source type.</param> /// <param name="targetType">The target type.</param> /// <param name="serializer">The serializer to use to deserialize the value.</param> public static void EmitConvertValue(ILGenerator il, string memberName, Type sourceType, Type targetType, IDbObjectSerializer serializer) { // targetType - the target type we need to convert to // underlyingTargetType - if the target type is nullable, we need to look at the underlying target type // rawTargetType - if the underlying target type is enum, we need to look at the underlying target type for that // sourceType - this is the type of the data in the data set Type underlyingTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType; // some labels that we need var finishLabel = il.DefineLabel(); Label isDbNullLabel = il.DefineLabel(); // if the value is DbNull, then we continue to the next item il.Emit(OpCodes.Dup); // dup value, stack => [target][value][value] il.Emit(OpCodes.Isinst, typeof(DBNull)); // isinst DBNull:value, stack => [target][value-as-object][DBNull or null] il.Emit(OpCodes.Brtrue_S, isDbNullLabel); // br.true isDBNull, stack => [target][value-as-object] // handle the special target types first if (targetType == typeof(char)) { // char il.EmitCall(OpCodes.Call, _readChar, null); } else if (targetType == typeof(char?)) { // char? il.EmitCall(OpCodes.Call, _readNullableChar, null); } else if (targetType == TypeHelper.LinqBinaryType) { // unbox sql byte arrays to Linq.Binary // before: stack => [target][object-value] // after: stack => [target][byte-array-value] il.Emit(OpCodes.Unbox_Any, typeof(byte[])); // stack is now [target][byte-array] // before: stack => [target][byte-array-value] // after: stack => [target][Linq.Binary-value] il.Emit(OpCodes.Newobj, TypeHelper.LinqBinaryCtor); } else if (targetType == typeof(XmlDocument)) { // special handler for XmlDocuments // before: stack => [target][object-value] il.Emit(OpCodes.Call, _readXmlDocument); // after: stack => [target][xmlDocument] } else if (targetType == typeof(XDocument)) { // special handler for XDocuments // before: stack => [target][object-value] il.Emit(OpCodes.Call, _readXDocument); // after: stack => [target][xDocument] } else if (serializer != null && serializer.CanDeserialize(sourceType, targetType)) { // we are getting a string from the database, but the target is not a string, and it's a reference type // assume the column is a serialized data type and that we want to deserialize it il.EmitLoadType(targetType); StaticFieldStorage.EmitLoad(il, serializer); il.Emit(OpCodes.Call, typeof(TypeConverterGenerator).GetMethod("DeserializeObject", BindingFlags.NonPublic | BindingFlags.Static)); il.Emit(OpCodes.Unbox_Any, targetType); } else if (underlyingTargetType.IsEnum && sourceType == typeof(string)) { var localString = il.DeclareLocal(typeof(string)); // if we are converting a string to an enum, then parse it. // see if the value from the database is a string. if so, we need to parse it. If not, we will just try to unbox it. il.Emit(OpCodes.Isinst, typeof(string)); // is string, stack => [target][string] il.Emit(OpCodes.Stloc, localString); // pop loc.2 (enum), stack => [target] // call enum.parse (type, value, true) il.EmitLoadType(underlyingTargetType); il.Emit(OpCodes.Ldloc, localString); // push enum, stack => [target][enum-type][string] il.Emit(OpCodes.Ldc_I4_1); // push true, stack => [target][enum-type][string][true] il.EmitCall(OpCodes.Call, _enumParse, null); // call Enum.Parse, stack => [target][enum-as-object] // Enum.Parse returns an object, which we need to unbox to the enum value il.Emit(OpCodes.Unbox_Any, underlyingTargetType); } else if (EmitConstructorConversion(il, sourceType, targetType)) { // target type can be constructed from source type } else { // this isn't a system value type, so unbox to the type the reader is giving us (this is a system type, hopefully) // now we have an unboxed sourceType il.Emit(OpCodes.Unbox_Any, sourceType); if (sourceType != targetType) { // attempt to convert the value to the target type if (!EmitConversionOrCoersion(il, sourceType, targetType)) { if (sourceType != targetType) { throw new InvalidOperationException(String.Format( CultureInfo.InvariantCulture, "Field {0} cannot be converted from {1} to {2}. Create a conversion constructor or conversion operator.", memberName, sourceType.AssemblyQualifiedName, targetType.AssemblyQualifiedName)); } } // if the target is nullable, then construct the nullable from the data if (Nullable.GetUnderlyingType(targetType) != null) { il.Emit(OpCodes.Newobj, targetType.GetConstructor(new[] { underlyingTargetType })); } } } ///////////////////////////////////////////////////////////////////// // convert DBNull to default of the given type il.Emit(OpCodes.Br_S, finishLabel); il.MarkLabel(isDbNullLabel); il.Emit(OpCodes.Pop); TypeHelper.EmitDefaultValue(il, targetType); il.MarkLabel(finishLabel); }
private static Action <IDbCommand, object> CreateClassInputParameterGenerator(IDbCommand command, Type type) { var provider = InsightDbProvider.For(command); var parameters = provider.DeriveParameters(command); // special case if the parameters object is an IEnumerable or Array // look for the parameter that is a Structured object and pass the array to the TVP // note that string supports ienumerable, so exclude atomic types var enumerable = type.GetInterfaces().FirstOrDefault(i => i.GetTypeInfo().IsGenericType&& i.GetGenericTypeDefinition() == typeof(IEnumerable <>)); if (enumerable != null && type != typeof(string) && parameters.OfType <IDataParameter>().Where(p => p.Direction.HasFlag(ParameterDirection.Input)).Count() == 1) { return((IDbCommand cmd, object o) => { // don't use the provider above. The command may be unwrapped by the time we get back here var tableParameter = InsightDbProvider.For(cmd).CloneParameter(cmd, parameters.OfType <IDataParameter>().Single(p => p.Direction.HasFlag(ParameterDirection.Input))); cmd.Parameters.Add(tableParameter); ListParameterHelper.ConvertListParameter(tableParameter, o, cmd); }); } // get the mapping of the properties for the type var mappings = ColumnMapping.MapParameters(type, command, parameters); // start creating a dynamic method Type typeOwner = type.HasElementType ? type.GetElementType() : type; var dm = new DynamicMethod(String.Format(CultureInfo.InvariantCulture, "CreateInputParameters-{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, typeOwner, true); var il = dm.GetILGenerator(); // copy the parameters into the command object var parametersLocal = il.DeclareLocal(typeof(IDataParameter[])); StaticFieldStorage.EmitLoad(il, provider); il.Emit(OpCodes.Ldarg_0); StaticFieldStorage.EmitLoad(il, parameters); il.Emit(OpCodes.Call, typeof(InsightDbProvider).GetMethod("CopyParameters", BindingFlags.NonPublic | BindingFlags.Instance)); il.Emit(OpCodes.Stloc, parametersLocal); // go through all of the mappings for (int i = 0; i < mappings.Count; i++) { var mapping = mappings[i]; var dbParameter = parameters[i]; // if there is no mapping for the parameter if (mapping == null) { // sql will silently eat table parameters that are not specified, and that can be difficult to debug if (provider.IsTableValuedParameter(command, dbParameter)) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Table parameter {0} must be specified", dbParameter.ParameterName)); } // unspecified input parameters get skipped if (dbParameter.Direction == ParameterDirection.Input) { parameters[i] = null; } continue; } var memberType = mapping.Member.MemberType; var serializer = mapping.Serializer; // get the parameter il.Emit(OpCodes.Ldloc, parametersLocal); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldelem, typeof(IDataParameter)); // look up the best type to use for the parameter DbType sqlType = LookupDbType(memberType, serializer, dbParameter.DbType); // give the provider an opportunity to fix up the template parameter (e.g. set UDT type names) provider.FixupParameter(command, dbParameter, sqlType, memberType, mapping.Member.SerializationMode); // give a chance to override the best guess parameter DbType overriddenSqlType = sqlType; if (sqlType != DbTypeEnumerable) { overriddenSqlType = ColumnMapping.MapParameterDataType(memberType, command, dbParameter, sqlType); } /////////////////////////////////////////////////////////////// // We have a parameter, start handling all of the other types /////////////////////////////////////////////////////////////// if (overriddenSqlType != sqlType) { sqlType = overriddenSqlType; dbParameter.DbType = sqlType; } /////////////////////////////////////////////////////////////// // Get the value from the object onto the stack /////////////////////////////////////////////////////////////// il.Emit(OpCodes.Ldarg_1); if (type.GetTypeInfo().IsValueType) { il.Emit(OpCodes.Unbox_Any, type); } /////////////////////////////////////////////////////////////// // Special case support for enumerables. If the type is -1 (our workaround, then call the list parameter method) /////////////////////////////////////////////////////////////// if (sqlType == DbTypeEnumerable) { // we have the parameter and the value as object, add the command ClassPropInfo.EmitGetValue(type, mapping.PathToMember, il); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, typeof(ListParameterHelper).GetMethod("ConvertListParameter", BindingFlags.Static | BindingFlags.NonPublic)); continue; } Label readyToSetLabel = il.DefineLabel(); ClassPropInfo.EmitGetValue(type, mapping.PathToMember, il, readyToSetLabel); // special conversions for timespan to datetime if ((sqlType == DbType.Time && dbParameter.DbType != DbType.Time) || (dbParameter.DbType == DbType.DateTime || dbParameter.DbType == DbType.DateTime2 || dbParameter.DbType == DbType.DateTimeOffset)) { IlHelper.EmitLdInt32(il, (int)dbParameter.DbType); il.Emit(OpCodes.Call, typeof(TypeConverterGenerator).GetMethod("ObjectToSqlDateTime")); } // if it's class type, boxed value type (in an object), or nullable, then we have to check for null var nullableUnderlyingType = Nullable.GetUnderlyingType(memberType); if (!memberType.GetTypeInfo().IsValueType || nullableUnderlyingType != null) { Label notNull = il.DefineLabel(); // check to see if it's not null il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brtrue, notNull); // it's null. replace the value with DbNull il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldsfld, _dbNullValue); // value is set to null. ready to set the property. il.Emit(OpCodes.Br, readyToSetLabel); // we know the value is not null il.MarkLabel(notNull); } // some providers (notably npgsql > 4.0) don't convert enums to ints, so we do it for them if ((memberType != null && memberType.GetTypeInfo() != null && memberType.GetTypeInfo().IsEnum) || (nullableUnderlyingType != null && nullableUnderlyingType.GetTypeInfo() != null && nullableUnderlyingType.GetTypeInfo().IsEnum)) { var enumType = nullableUnderlyingType ?? memberType; // ClassPropInfo.EmitGetValue has the enum boxed, so unbox, cast, and re-box switch (dbParameter.DbType) { case DbType.Int16: il.Emit(OpCodes.Unbox_Any, enumType); il.Emit(OpCodes.Conv_I2); il.Emit(OpCodes.Box, typeof(Int16)); break; case DbType.Int32: il.Emit(OpCodes.Unbox_Any, enumType); il.Emit(OpCodes.Conv_I4); il.Emit(OpCodes.Box, typeof(Int32)); break; case DbType.Int64: il.Emit(OpCodes.Unbox_Any, enumType); il.Emit(OpCodes.Conv_I8); il.Emit(OpCodes.Box, typeof(Int64)); break; } } /////////////////////////////////////////////////////////////// // if this is a linq binary, convert it to a byte array /////////////////////////////////////////////////////////////// if (memberType == TypeHelper.LinqBinaryType) { il.Emit(OpCodes.Callvirt, TypeHelper.LinqBinaryToArray); } else if (memberType == typeof(XmlDocument)) { // we are sending up an XmlDocument. ToString just returns the classname, so use the outerxml. il.Emit(OpCodes.Callvirt, memberType.GetProperty("OuterXml").GetGetMethod()); } else if (memberType == typeof(XDocument)) { // we are sending up an XDocument. Use ToString. il.Emit(OpCodes.Callvirt, memberType.GetMethod("ToString", new Type[] { })); } else if (serializer != null && serializer.CanSerialize(memberType, sqlType)) { il.EmitLoadType(memberType); StaticFieldStorage.EmitLoad(il, serializer); il.Emit(OpCodes.Call, typeof(DbParameterGenerator).GetMethod("SerializeParameterValue", BindingFlags.NonPublic | BindingFlags.Static)); } /////////////////////////////////////////////////////////////// // p.Value = value /////////////////////////////////////////////////////////////// // push parameter is at top of method // value is above il.MarkLabel(readyToSetLabel); if (memberType == typeof(string)) { il.Emit(OpCodes.Call, typeof(DbParameterGenerator).GetMethod("SetParameterStringValue", BindingFlags.NonPublic | BindingFlags.Static)); } else if ((memberType == typeof(Guid?) || (memberType == typeof(Guid))) && dbParameter.DbType != DbType.Guid && command.CommandType == CommandType.StoredProcedure) { il.Emit(OpCodes.Call, typeof(DbParameterGenerator).GetMethod("SetParameterGuidValue", BindingFlags.NonPublic | BindingFlags.Static)); } else { il.Emit(OpCodes.Callvirt, _iDataParameterSetValue); } } il.Emit(OpCodes.Ret); return((Action <IDbCommand, object>)dm.CreateDelegate(typeof(Action <IDbCommand, object>))); }
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 }; } }