public StreamingObjectConstructor(DbDataReader source, ConstructorMetadata?constructor, IReadOnlyList <ColumnMetadata> nonNullableColumns, MaterializerTypeConverter converter) { m_Converter = converter ?? new(); m_Source = source; m_Ordinals = new Dictionary <string, int>(source.FieldCount, StringComparer.OrdinalIgnoreCase); m_NullableColumns = new Dictionary <int, bool>(source.FieldCount); for (var i = 0; i < source.FieldCount; i++) { var columnName = source.GetName(i); m_Ordinals.Add(columnName, i); m_NullableColumns.Add(i, !nonNullableColumns.Any(c => c.SqlName == columnName)); //assume nullable unless proven otherwise } if (constructor == null) { constructor = MetadataCache.GetMetadata(typeof(T)).Constructors.Find(s_DefaultConstructor); } if (constructor == null) { throw new MappingException($"Cannot find a default constructor for {typeof(T).Name}"); } var desiredType = typeof(T); m_Constructor = constructor; var constructorParameters = m_Constructor.ParameterNames; for (var i = 0; i < constructorParameters.Length; i++) { if (!m_Ordinals.ContainsKey(constructorParameters[i])) { throw new MappingException($"Cannot find a column that matches the parameter {constructorParameters[i]}"); } } m_Dictionary = new StreamingObjectConstructorDictionary(this); if (constructor.Signature.Length == 0) { m_MappedProperties = new List <OrdinalMappedProperty <T> >(); foreach (var mapper in s_AllMappedProperties) { if (m_Dictionary.ContainsKey(mapper.MappedColumnName)) { m_MappedProperties.Add(new OrdinalMappedProperty <T>(mapper, m_Ordinals[mapper.MappedColumnName])); } } } }
/// <summary> /// Creates a new Table from an IDataReader /// </summary> /// <param name="source">The source.</param> /// <exception cref="ArgumentNullException">nameof(s - rce), "source is</exception> /// <exception cref="ArgumentException">No columns were returned - source</exception> /// <param name="converter">The type converter.</param> public Table(DbDataReader source, MaterializerTypeConverter converter) { if (source == null) { throw new ArgumentNullException(nameof(source), $"{nameof(source)} is null."); } if (source.FieldCount == 0) { throw new ArgumentException("No columns were returned", nameof(source)); } m_Converter = converter ?? new(); TableName = ""; var cols = new List <string>(source.FieldCount); var colTypes = new Dictionary <string, Type>(source.FieldCount, StringComparer.OrdinalIgnoreCase); for (var i = 0; i < source.FieldCount; i++) { cols.Add(source.GetName(i)); colTypes.Add(source.GetName(i), source.GetFieldType(i)); } m_Columns = new ReadOnlyCollection <string>(cols); m_ColumnTypes = new ReadOnlyDictionary <string, Type>(colTypes); var rows = new Collection <Row>(); while (source.Read()) { var row = new Dictionary <string, object?>(source.FieldCount, StringComparer.OrdinalIgnoreCase); for (var i = 0; i < source.FieldCount; i++) { object?temp = source[i]; if (temp == DBNull.Value) { temp = null; } row.Add(source.GetName(i), temp); } rows.Add(new Row(row)); } m_Rows = new RowCollection(rows); }
/// <summary> /// Initializes a new instance of the <see cref="TableSet" /> class. /// </summary> /// <param name="reader">The data reader used to populate this TableSet.</param> /// <param name="converter">The type converter.</param> /// <param name="tableNames">Optional list of table names.</param> /// <exception cref="System.ArgumentNullException">reader</exception> public TableSet(DbDataReader reader, MaterializerTypeConverter converter, params string[] tableNames) { if (reader == null) { throw new ArgumentNullException(nameof(reader), $"{nameof(reader)} is null."); } if (tableNames == null) { tableNames = Array.Empty <string>(); } var index = 0; do { var tableName = (index < tableNames.Length) ? tableNames[index] : ("Table " + index); m_Internal.Add(new Table(tableName, reader, converter)); index += 1; }while (reader.NextResult()); }
/// <summary> /// Creates a new NamedTable from an IDataReader /// </summary> /// <param name="tableName">Name of the table.</param> /// <param name="source">The source.</param> /// <param name="converter">The type converter.</param> public Table(string tableName, DbDataReader source, MaterializerTypeConverter converter) : this(source, converter) { TableName = tableName; }
static object?SetProperty <T>(T target, PropertyMetadata property, object?value, OrdinalMappedProperty <T>?mapper, MaterializerTypeConverter converter) where T : class { if (target == null) { throw new ArgumentNullException(nameof(target), $"{nameof(target)} is null."); } if (property == null) { throw new ArgumentNullException(nameof(property), $"{nameof(property)} is null."); } var targetType = property.PropertyType; if (value != null && targetType != value.GetType()) { if (!converter.TryConvertType(targetType, ref value, out var conversionException)) { throw new MappingException($"Cannot map value of type {value!.GetType().FullName} to property {property.Name} of type {targetType.Name}.", conversionException); } } if (mapper == null || value == null) { property.InvokeSet(target, value); } else { mapper.InvokeSet(target, value); } return(value); }
static void SetDecomposedProperty(IReadOnlyDictionary <string, object?> source, object target, string?decompositionPrefix, PropertyMetadata property, MaterializerTypeConverter converter) { object?child = null; if (property.CanRead) { child = property.InvokeGet(target); } if (child == null && property.CanWrite && property.PropertyType.GetConstructor(Array.Empty <Type>()) != null) { child = Activator.CreateInstance(property.PropertyType); property.InvokeSet(target, child); } if (child != null) { PopulateComplexObject(source, child, decompositionPrefix + property.DecompositionPrefix, converter); } }
static internal void PopulateComplexObject <T>(IReadOnlyDictionary <int, object?> source, T target, string?decompositionPrefix, IList <OrdinalMappedProperty <T> > mappedProperties, IList <MappedProperty <T> > decomposedProperties, MaterializerTypeConverter converter) where T : class { if (source == null) { throw new ArgumentNullException(nameof(source), $"{nameof(source)} is null."); } if (target == null) { throw new ArgumentNullException(nameof(target), $"{nameof(target)} is null."); } foreach (var property in mappedProperties) { var value = source[property.Ordinal]; SetProperty(target, property.PropertyMetadata, value, property, converter); } foreach (var property in decomposedProperties) { SetDecomposedProperty((IReadOnlyDictionary <string, object?>)source, target, decompositionPrefix, property.PropertyMetadata, converter); } }
static internal void PopulateComplexObject <T>(IReadOnlyDictionary <string, object?> source, T target, string?decompositionPrefix, MaterializerTypeConverter converter) where T : class { if (source == null) { throw new ArgumentNullException(nameof(source), $"{nameof(source)} is null."); } if (target == null) { throw new ArgumentNullException(nameof(target), $"{nameof(target)} is null."); } foreach (var property in MetadataCache.GetMetadata(target.GetType()).Properties) { string mappedColumnName = decompositionPrefix + property.MappedColumnName; if (property.CanWrite && source.ContainsKey(mappedColumnName)) { var value = source[mappedColumnName]; value = SetProperty(target, property, value, null, converter); } else if (property.Decompose) { SetDecomposedProperty(source, target, decompositionPrefix, property, converter); } } }
static internal T ConstructObject <T>(IReadOnlyDictionary <string, object?> source, ConstructorMetadata?constructor, MaterializerTypeConverter converter, bool?populateComplexObject = null) where T : class { //If we didn't get a constructor, look for a default constructor to use. if (constructor == null) { constructor = MetadataCache.GetMetadata(typeof(T)).Constructors.Find(s_EmptyTypeList); } if (constructor == null) { throw new MappingException($"Cannot find a default constructor for {typeof(T).Name}"); } //populate properties when using a default constructor if (!populateComplexObject.HasValue) { populateComplexObject = constructor.Signature.Length == 0; } //Make sure we have all of the requested parameters var constructorParameters = constructor.ParameterNames; for (var i = 0; i < constructorParameters.Length; i++) { if (!source.ContainsKey(constructorParameters[i])) { throw new MappingException($"Cannot find a column that matches the parameter {constructorParameters[i]}"); } } //Build the constructor var parameters = new object?[constructorParameters.Length]; for (var i = 0; i < constructorParameters.Length; i++) { parameters[i] = source[constructorParameters[i]]; } var result = (T)constructor.ConstructorInfo.Invoke(parameters); if (populateComplexObject.Value) //Populate properties and child objects { PopulateComplexObject(source, result, null, converter); } //Change tracking objects should be materialized as unchanged. (result as IChangeTracking)?.AcceptChanges(); return(result); }
internal static StreamingObjectConstructor <T> AsObjectConstructor <T>(this DbDataReader reader, IReadOnlyList <Type>?constructorSignature, IReadOnlyList <ColumnMetadata> nonNullableColumns, MaterializerTypeConverter converter) where T : class { return(new StreamingObjectConstructor <T>(reader, constructorSignature, nonNullableColumns, converter)); }