/// <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>));
				}
			}
		}