/// <summary> /// Initializes a new instance of the ObjectReader class. /// </summary> /// <param name="identity">The schema identity to analyze.</param> /// <param name="reader">The reader that contains the schema.</param> /// <returns>A list of accessor functions to get values from the type.</returns> private ObjectReader(SchemaMappingIdentity identity, IDataReader reader) { // SQL Server tells us the precision of the columns // but the TDS parser doesn't like the ones set on money, smallmoney and date // so we have to override them SchemaTable = reader.GetSchemaTable(); SchemaTable.Columns["NumericScale"].ReadOnly = false; foreach (DataRow row in SchemaTable.Rows) { string dataType = row["DataTypeName"].ToString(); if (String.Equals(dataType, "money", StringComparison.OrdinalIgnoreCase)) row["NumericScale"] = 4; else if (String.Equals(dataType, "smallmoney", StringComparison.OrdinalIgnoreCase)) row["NumericScale"] = 4; else if (String.Equals(dataType, "date", StringComparison.OrdinalIgnoreCase)) row["NumericScale"] = 0; } IsAtomicType = TypeHelper.IsAtomicType(identity.Graph); if (!IsAtomicType) { // get the type we are binding to Type type = identity.Graph.IsSubclassOf(typeof(Graph)) ? identity.Graph.GetGenericArguments()[0] : identity.Graph; var mapping = ColumnMapping.Tables.CreateMapping(type, reader, null, null, null, 0, reader.FieldCount, uniqueMatches: true); Accessors = new Func<object, object>[mapping.Length]; MemberTypes = new Type[mapping.Length]; for (int i = 0; i < mapping.Length; i++) { ClassPropInfo propInfo = mapping[i]; if (propInfo == null) continue; // 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 if (type.IsValueType) { // access the field/property of a value type il.DeclareLocal(type); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Unbox_Any, type); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloca_S, (byte)0); } else { // access the field/property of a reference type il.Emit(OpCodes.Ldarg_0); // push object argument il.Emit(OpCodes.Isinst, type); // cast object -> type } // get the value from the object propInfo.EmitGetValue(il); propInfo.EmitBox(il); il.Emit(OpCodes.Ret); MemberTypes[i] = propInfo.MemberType; Accessors[i] = (Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>)); } } }
private ObjectReader(IDbCommand command, SchemaMappingIdentity identity, IDataReader reader) { var provider = InsightDbProvider.For(command); SchemaTable = reader.GetSchemaTable(); // SQL Server tells us the precision of the columns // but the TDS parser doesn't like the ones set on money, smallmoney and date // so we have to override them if (SchemaTable.Columns.Contains("DataTypeName")) { SchemaTable.Columns["NumericScale"].ReadOnly = false; foreach (DataRow row in SchemaTable.Rows) { string dataType = row["DataTypeName"].ToString(); if (String.Equals(dataType, "money", StringComparison.OrdinalIgnoreCase)) row["NumericScale"] = 4; else if (String.Equals(dataType, "smallmoney", StringComparison.OrdinalIgnoreCase)) row["NumericScale"] = 4; else if (String.Equals(dataType, "date", StringComparison.OrdinalIgnoreCase)) row["NumericScale"] = 0; } } // get the type we are binding to Type type = identity.Graph.IsSubclassOf(typeof(Graph)) ? identity.Graph.GetGenericArguments()[0] : identity.Graph; IsAtomicType = TypeHelper.IsAtomicType(identity.Graph); if (!IsAtomicType) { var mapping = ColumnMapping.Tables.CreateMapping(type, reader, null, null, null, 0, reader.FieldCount, uniqueMatches: true); _accessors = new Func<object, object>[mapping.Length]; _memberNames = new string[mapping.Length]; _memberTypes = new Type[mapping.Length]; for (int i = 0; i < mapping.Length; i++) { ClassPropInfo propInfo = mapping[i]; if (propInfo == null) continue; // 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 the provider type is Xml, then serialize the value if (!sourceType.IsValueType && provider.IsXmlColumn(command, SchemaTable, i)) { il.EmitLoadType(sourceType); il.Emit(OpCodes.Call, typeof(TypeHelper).GetMethod("SerializeObjectToXml", new Type[] { typeof(object), typeof(Type) })); } 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); _memberNames[i] = propInfo.Name; _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 _memberNames = new string[1] { "value" }; _memberTypes = new Type[1] { type }; _accessors = new Func<object, object>[] { o => o }; } }
/// <summary> /// Returns an object reader for the given type that matches the given schema. /// </summary> /// <param name="reader">The reader containing the schema to analyze.</param> /// <param name="type">The type to analyze.</param> /// <returns>An ObjectReader for the schema and type.</returns> public static ObjectReader GetObjectReader(IDataReader reader, Type type) { SchemaIdentity schemaIdentity = new SchemaIdentity(reader); SchemaMappingIdentity mappingIdentity = new SchemaMappingIdentity(schemaIdentity, type, null, SchemaMappingType.ExistingObject); return _readerDataCache.GetOrAdd(mappingIdentity, i => new ObjectReader(i, reader)); }
/// <summary> /// Initializes a new instance of the ObjectReader class. /// </summary> /// <param name="identity">The schema identity to analyze.</param> /// <param name="reader">The reader that contains the schema.</param> /// <returns>A list of accessor functions to get values from the type.</returns> private ObjectReader(SchemaMappingIdentity identity, IDataReader reader) { // SQL Server tells us the precision of the columns // but the TDS parser doesn't like the ones set on money, smallmoney and date // so we have to override them SchemaTable = reader.GetSchemaTable(); SchemaTable.Columns["NumericScale"].ReadOnly = false; foreach (DataRow row in SchemaTable.Rows) { string dataType = row["DataTypeName"].ToString(); if (String.Equals(dataType, "money", StringComparison.OrdinalIgnoreCase)) row["NumericScale"] = 4; else if (String.Equals(dataType, "smallmoney", StringComparison.OrdinalIgnoreCase)) row["NumericScale"] = 4; else if (String.Equals(dataType, "date", StringComparison.OrdinalIgnoreCase)) row["NumericScale"] = 0; } IsAtomicType = TypeHelper.IsAtomicType(identity.Graph); if (!IsAtomicType) { // get the type we are binding to Type type = identity.Graph.IsSubclassOf(typeof(Graph)) ? identity.Graph.GetGenericArguments()[0] : identity.Graph; var mapping = ColumnMapping.Tables.CreateMapping(type, reader, null, null, null, 0, reader.FieldCount, uniqueMatches: true); Accessors = new Func<object, object>[mapping.Length]; MemberTypes = new Type[mapping.Length]; for (int i = 0; i < mapping.Length; i++) { ClassPropInfo propInfo = mapping[i]; if (propInfo == null) continue; // 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.DeclareLocal(type); // loc.0 - for calling methods on struct objects il.DeclareLocal(propInfo.MemberType); // loc.1 - for calling methods on nullable values // convert the object reference to the desired type if (type.IsValueType) { // access the field/property of a value type il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Unbox_Any, type); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloca_S, (byte)0); } else { // access the field/property of a reference type il.Emit(OpCodes.Ldarg_0); // push object argument 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(); il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Ldloca_S, (int)1); 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_S, (int)1); il.Emit(OpCodes.Call, sourceType.GetProperty("Value").GetGetMethod()); // at this point we have de-nulled value, so use those converters sourceType = underlyingType; } // see if there is an conversion operator on either the source or target types MethodInfo mi = TypeConverterGenerator.FindConversionMethod(sourceType, targetType); if (mi != null) { // convert the object's member type to the target member type and box it il.Emit(OpCodes.Call, mi); il.Emit(OpCodes.Box, targetType); } else if (TypeConverterGenerator.EmitCoersion(il, sourceType, targetType)) { // we convert primitives to the proper type // now we box it aas a target object il.Emit(OpCodes.Box, targetType); } else { // wrap the object as its source type il.Emit(OpCodes.Box, sourceType); } il.Emit(OpCodes.Ret); MemberTypes[i] = propInfo.MemberType; Accessors[i] = (Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>)); } } }