/// <summary> /// Creates a deserializer to deserialize an object from an IDataReader. /// </summary> /// <param name="reader">The reader to analyze.</param> /// <param name="type">The type of object to deserialize from the IDataReader.</param> /// <param name="structure">The structure of the object.</param> /// <param name="mappingType">The type of mapping to create.</param> /// <returns> /// A function that takes an IDataReader and deserializes an object of type T. /// The first parameter will be an IDataReader. /// If createNewObject is true, the next parameter will be of type T. /// If useCallback is true, the next parameter will be an Action<object[]>. /// </returns> public static Delegate CreateDeserializer(IDataReader reader, Type type, IRecordStructure structure, SchemaMappingType mappingType) { // process the object graph types var subTypes = structure.GetObjectTypes(); if (subTypes[0] != type) throw new ArgumentException("The top-level type of the object graph must match the return type of the object.", "structure"); // if the graph type is not a graph, or just the object, and we don't want a callback function // then just return a one-level graph. if (subTypes.Length == 1 && !mappingType.HasFlag(SchemaMappingType.WithCallback)) return CreateClassDeserializer(type, reader, structure, 0, (reader.IsClosed) ? 0 : reader.FieldCount, mappingType.HasFlag(SchemaMappingType.NewObject)); // we can't deserialize an object graph in an insert/merge because we don't know whether to create subobjects or leave them null. if (!mappingType.HasFlag(SchemaMappingType.NewObject)) throw new ArgumentException("mappingType must be set to NewObject when deserializing an object graph.", "mappingType"); // create the graph deserializer if (mappingType.HasFlag(SchemaMappingType.WithCallback)) return CreateGraphDeserializerWithCallback(subTypes, reader, structure); else return CreateGraphDeserializer(subTypes, reader, structure); }
/// <summary> /// Initializes a new instance of the SchemaMappingIdentity class. /// </summary> /// <param name="schemaIdentity">The identity of the schema to map to.</param> /// <param name="recordReader">The reader to use to read the objects from the stream.</param> /// <param name="mappingType">The type of mapping operation.</param> public SchemaMappingIdentity(SchemaIdentity schemaIdentity, IRecordReader recordReader, SchemaMappingType mappingType) { if (recordReader == null) { throw new ArgumentNullException("recordReader"); } // save the values away for later RecordReader = recordReader; _mappingType = mappingType; _schemaIdentity = schemaIdentity; // we know that we are going to store this in a hashtable, so pre-calculate the hashcode unchecked { // base the hashcode on the mapping type, target graph, and schema contents _hashCode = (int)_mappingType; _hashCode *= 13; _hashCode += RecordReader.GetHashCode(); _hashCode *= 13; _hashCode += schemaIdentity.GetHashCode(); } }
/// <summary> /// Creates a deserializer to deserialize an object from an IDataReader. /// </summary> /// <typeparam name="T">The type of object to be returned from the function.</typeparam> /// <param name="reader">The reader to analyze.</param> /// <param name="type">The type of object to deserialize from the IDataReader.</param> /// <param name="withGraph">The type of the graph of object to be returned, or null/typeof(T) to deserialize just the top-level object.</param> /// <param name="idColumns">An optional mapping of type to Id columns used for splitting and object graph.</param> /// <param name="mappingType">The type of mapping to create.</param> /// <returns> /// A function that takes an IDataReader and deserializes an object of type T. /// The first parameter will be an IDataReader. /// If createNewObject is true, the next parameter will be of type T. /// If useCallback is true, the next parameter will be an Action<object[]>. /// </returns> public static Delegate CreateDeserializer(IDataReader reader, Type type, Type withGraph, Dictionary<Type, string> idColumns, SchemaMappingType mappingType) { // if the graph isn't specified, look for a defaultgraphattribute, or just do a one-level graph if (withGraph == null) { DefaultGraphAttribute defaultGraph = type.GetCustomAttributes(typeof(DefaultGraphAttribute), true).OfType<DefaultGraphAttribute>().FirstOrDefault(); if (defaultGraph != null) withGraph = defaultGraph.GetGraphTypes()[0]; else withGraph = typeof(Graph<>).MakeGenericType(type); } // make sure that withGraph is a graph if (!withGraph.IsSubclassOf(typeof(Graph))) throw new ArgumentException("withGraph passed in must be of Graph<T>", "withGraph"); // process the object graph types Type[] subTypes = withGraph.GetGenericArguments(); if (subTypes[0] != type) throw new ArgumentException("The top-level type of the object graph must match the return type of the object.", "withGraph"); // if the graph type is not a graph, or just the object, and we don't want a callback function // then just return a one-level graph. if (subTypes.Length == 1 && !mappingType.HasFlag(SchemaMappingType.WithCallback)) return CreateClassDeserializer(type, reader, 0, reader.FieldCount, mappingType.HasFlag(SchemaMappingType.NewObject)); // we can't deserialize an object graph in an insert/merge because we don't know whether to create subobjects or leave them null. if (!mappingType.HasFlag(SchemaMappingType.NewObject)) throw new ArgumentException("mappingType must be set to NewObject when deserializing an object graph.", "mappingType"); // create the graph deserializer if (mappingType.HasFlag(SchemaMappingType.WithCallback)) return CreateGraphDeserializerWithCallback(subTypes, reader, idColumns); else return CreateGraphDeserializer(subTypes, reader, idColumns); }
/// <summary> /// Get a deserializer to read class T from the given reader. /// </summary> /// <param name="reader">The reader to read from.</param> /// <param name="type">The type of object to deserialize.</param> /// <param name="structure">The structure of the objects in the record.</param> /// <param name="mappingType">The type of mapping to return.</param> /// <returns>A function that can deserialize a T from the reader.</returns> private static Delegate GetDeserializer(IDataReader reader, Type type, IRecordStructure structure, SchemaMappingType mappingType) { if (structure == null) { throw new ArgumentNullException("structure"); } // This method should try to return the deserializer with as little work as possible. // Calculating the SchemaMappingIdentity is relatively expensive, so we will take care of the simple cases first, // Where we can just look up a type in a dictionary. // since these types are single column types, deserializing these types do not depend on the schema that comes back from the database // we don't need to keep a schema identity for these Delegate deserializer = null; if (!_simpleDeserializers.TryGetValue(type, out deserializer) && TypeHelper.IsAtomicType(type)) { deserializer = GetValueDeserializer(type); } // we have a simple deserializer if (deserializer != null) { var genericArgs = structure.GetObjectTypes(); if (genericArgs.Length != 1 || genericArgs[0] != type) { throw new ArgumentException("Column Mapper does not match single column deserialization", "structure"); } return(deserializer); } // at this point, we know that we aren't returning a value type or simple object that doesn't depend on the schema. // so we need to calculate a mapping identity and then create or return a deserializer. SchemaMappingIdentity identity = new SchemaMappingIdentity(reader, structure, mappingType); // try to get the deserializer. if not found, create one. return(_deserializers.GetOrAdd( identity, key => ClassDeserializerGenerator.CreateDeserializer(reader, type, structure, mappingType))); }
/// <summary> /// Creates a deserializer to deserialize an object from an IDataReader. /// </summary> /// <param name="reader">The reader to analyze.</param> /// <param name="type">The type of object to deserialize from the IDataReader.</param> /// <param name="structure">The structure of the object.</param> /// <param name="mappingType">The type of mapping to create.</param> /// <returns> /// A function that takes an IDataReader and deserializes an object of type T. /// The first parameter will be an IDataReader. /// If createNewObject is true, the next parameter will be of type T. /// If useCallback is true, the next parameter will be an Action<object[]>. /// </returns> public static Delegate CreateDeserializer(IDataReader reader, Type type, IRecordStructure structure, SchemaMappingType mappingType) { // process the object graph types var subTypes = structure.GetObjectTypes(); if (subTypes[0] != type) { throw new ArgumentException("The top-level type of the object graph must match the return type of the object.", "structure"); } // if the graph type is not a graph, or just the object, and we don't want a callback function // then just return a one-level graph. if (subTypes.Length == 1 && !mappingType.HasFlag(SchemaMappingType.WithCallback)) { return(CreateClassDeserializer(type, reader, structure, 0, (reader.IsClosed) ? 0 : reader.FieldCount, mappingType.HasFlag(SchemaMappingType.NewObject))); } // we can't deserialize an object graph in an insert/merge because we don't know whether to create subobjects or leave them null. if (!mappingType.HasFlag(SchemaMappingType.NewObject)) { throw new ArgumentException("mappingType must be set to NewObject when deserializing an object graph.", "mappingType"); } // create the graph deserializer if (mappingType.HasFlag(SchemaMappingType.WithCallback)) { return(CreateGraphDeserializerWithCallback(subTypes, reader, structure, mappingType == SchemaMappingType.ExistingObject)); } else { return(CreateGraphDeserializer(subTypes, reader, structure, mappingType == SchemaMappingType.ExistingObject)); } }
/// <summary> /// Initializes a new instance of the SchemaMappingIdentity class. /// </summary> /// <param name="reader">The reader to construct from.</param> /// <param name="recordReader">The reader to use to read the objects from the stream.</param> /// <param name="mappingType">The type of mapping operation.</param> public SchemaMappingIdentity(IDataReader reader, IRecordReader recordReader, SchemaMappingType mappingType) : this(new SchemaIdentity(reader), recordReader, mappingType) { }