/// <summary> /// Emits the code to get a value from a type, allowing dot accessors to select children. /// </summary> /// <param name="type">The base type.</param> /// <param name="memberName">The name of the member in the form a.b.c.d.</param> /// <param name="il">The ILGenerator to use,</param> /// <param name="readyToSetLabel">The label to jump to if any of the non-terminal accesses result (a.b.c) in null.</param> internal static void EmitGetValue(Type type, string memberName, ILGenerator il, Label?readyToSetLabel = null) { ClassPropInfo pi = null; var memberNames = SplitMemberName(memberName).ToArray(); Label nullLabel = il.DefineLabel(); // emit the code to get the value for (int i = 0; i < memberNames.Length; i++) { var member = memberNames[i]; // if the value on the stack can be null, do a null check here if (pi != null && !pi.MemberType.GetTypeInfo().IsValueType) { il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brfalse, nullLabel); } pi = FindMember(type, member); if (pi == null || !pi.CanGetMember) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Get method {0} not found on {1}", member, type)); } pi.EmitGetValue(il); type = pi.MemberType; } // emit the code to handle nulls var memberType = pi.MemberType; if (memberNames.Length > 1) { var doneLabel = il.DefineLabel(); il.Emit(OpCodes.Br, doneLabel); // if any of the prefix values are null, then we emit the default of the final type il.MarkLabel(nullLabel); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldnull); if (readyToSetLabel != null) { il.Emit(OpCodes.Br, readyToSetLabel.Value); } il.MarkLabel(doneLabel); } // if this is a value type, then box the value so the compiler can check the type and we can call methods on it if (memberType.GetTypeInfo().IsValueType) { il.Emit(OpCodes.Box, memberType); } }
/// <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 ObjectReader(IDbCommand command, Type type, IDataReader reader) { var provider = InsightDbProvider.For(command); // copy the schema and fix it SchemaTable = reader.GetSchemaTable().Copy(); FixupSchemaNumericScale(); FixupSchemaRemoveReadOnlyColumns(); IsAtomicType = TypeHelper.IsAtomicType(type); if (!IsAtomicType) { // create a mapping, and only keep mappings that match our modified schema var mappings = ColumnMapping.Tables.CreateMapping(type, reader, null, null, null, 0, reader.FieldCount, true) .Where(m => m != null).ToArray(); int columnCount = SchemaTable.Rows.Count; _accessors = new Func <object, object> [columnCount]; _memberTypes = new Type[columnCount]; for (int i = 0; i < columnCount; i++) { var columnName = SchemaTable.Rows[i]["ColumnName"].ToString(); var mapping = mappings.FirstOrDefault(m => String.Compare(m.ColumnName, columnName, StringComparison.OrdinalIgnoreCase) == 0); if (mapping == null) { continue; } ClassPropInfo propInfo = mapping.ClassPropInfo; // 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 propInfo.EmitGetValue(il); // 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)) { // if the provider type is Xml, then serialize the value var serializerMethod = mapping.Serializer.GetMethod("Serialize", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(object), typeof(Type) }, null); if (serializerMethod == null) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Serializer type {0} needs the method 'public static string Serialize(object, Type)'", mapping.Serializer.Name)); } il.EmitLoadType(sourceType); il.Emit(OpCodes.Call, serializerMethod); } 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.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 }; } }
private static DynamicMethod CreateClassDeserializerDynamicMethod(Type type, IDataReader reader, IRecordStructure structure, int startColumn, int columnCount, bool createNewObject, bool isRootObject, bool allowBindChild) { // if there are no columns detected for the class, then the deserializer is null if (columnCount == 0 && !isRootObject) { 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; // 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 localValues = new LocalBuilder[mappings.Count]; for (int index = 0; index < columnCount; index++) { var mapping = mappings[index]; if (mapping == null) { continue; } 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 (startColumn > 0) { 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 if (constructor != null) { foreach (var p in constructor.GetParameters()) { var mapping = mappings.Where(m => m != null).SingleOrDefault(m => m.Member.Name.IsIEqualTo(p.Name)); if (mapping != null) { il.Emit(OpCodes.Ldloc, localValues[mappings.IndexOf(mapping)]); } else { TypeHelper.EmitDefaultValue(il, 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; } var member = mapping.Member; if (!member.CanSetMember) { continue; } // don't set values that have already been set if (constructor != null && constructor.GetParameters().Any(p => p.Name.IsIEqualTo(mapping.Member.Name))) { 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); }
/// <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.GetTypeInfo().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>))); }
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>))); }
/// <summary> /// Create accessors to pull data from the object of the given type for the given schema. /// </summary> /// <param name="type">The type to analyze.</param> /// <param name="identity">The schema identity to analyze.</param> /// <returns>A list of accessor functions to get values from the type.</returns> private FieldReaderData CreateFieldReaderData(Type type, SchemaIdentity identity) { FieldReaderData readerData = new FieldReaderData(); readerData.Accessors = new List<Func<object, object>>(); readerData.MemberTypes = new List<Type>(); for (int i = 0; i < identity.SchemaTable.Rows.Count; i++) { // get the name of the column string name = _schemaTable.Rows[i]["ColumnName"].ToString(); // 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(); il.Emit(OpCodes.Ldarg_0); // push object argument il.Emit(OpCodes.Isinst, type); // cast object -> type // get the value from the object ClassPropInfo propInfo = new ClassPropInfo(type, name); propInfo.EmitGetValue(il); propInfo.EmitBox(il); il.Emit(OpCodes.Ret); readerData.MemberTypes.Add(propInfo.MemberType); readerData.Accessors.Add((Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>))); } return readerData; }