/// <summary> /// Execute the operation synchronously. /// </summary> /// <returns></returns> public override TArgument?Execute(object?state = null) { IReadOnlyDictionary <string, object?>?row = null; var executionToken = Prepare(); var rowCount = executionToken.Execute(cmd => { using (var reader = cmd.ExecuteReader(CommandBehavior)) { row = reader.ReadDictionary(); return((row != null ? 1 : 0) + reader.RemainingRowCount()); } }, state); if (rowCount == 0 || row == null) { throw new DataException("No rows were returned"); } else if (rowCount > 1) { throw new DataException($"Expected 1 row but received {rowCount} rows."); } //update the ArgumentValue with any new keys, calculated fields, etc. MaterializerUtilities.PopulateComplexObject(row, m_CommandBuilder.ArgumentValue, null, Converter); return(m_CommandBuilder.ArgumentValue); }
/// <summary> /// Execute the operation asynchronously. /// </summary> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="state">User defined state, usually used for logging.</param> /// <returns></returns> public override async Task <TArgument?> ExecuteAsync(CancellationToken cancellationToken, object?state = null) { IReadOnlyDictionary <string, object?>?row = null; var executionToken = Prepare(); var rowCount = await executionToken.ExecuteAsync(async cmd => { using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior, cancellationToken).ConfigureAwait(false)) { row = await reader.ReadDictionaryAsync().ConfigureAwait(false); return((row != null ? 1 : 0) + await reader.RemainingRowCountAsync().ConfigureAwait(false)); } }, cancellationToken, state).ConfigureAwait(false); if (rowCount == 0 || row == null) { throw new DataException("No rows were returned"); } else if (rowCount > 1) { throw new DataException($"Expected 1 row but received {rowCount} rows."); } //update the ArgumentValue with any new keys, calculated fields, etc. MaterializerUtilities.PopulateComplexObject(row, m_CommandBuilder.ArgumentValue, null, Converter); return(m_CommandBuilder.ArgumentValue); }
TObject CaptureOutputParameters(CommandExecutionToken <TCommand, TParameter> commandToken) { var result = AsOutputsMaterializer <TCommand, TParameter> .CaptureOutputParameters(commandToken); var objectResult = new TObject(); MaterializerUtilities.PopulateComplexObject(result, objectResult, null, Converter); return(objectResult); }
TObject ConstructObject(IReadOnlyDictionary <string, object?>?row, int?rowCount) { if (rowCount == 0 || row == null) { throw new MissingDataException($"No rows were returned. It was this expected, use `.ToObjectOrNull` instead of `.ToObject`."); } else if (rowCount > 1 && !m_RowOptions.HasFlag(RowOptions.DiscardExtraRows)) { throw new UnexpectedDataException($"Expected 1 row but received {rowCount} rows. If this was expected, use `RowOptions.DiscardExtraRows`."); } return(MaterializerUtilities.ConstructObject <TObject>(row, Constructor, Converter)); }
internal IEnumerable <KeyValuePair <Row, T> > ToObjectsWithEcho_New <T>() where T : new() { foreach (var row in Rows) { var item = new T(); MaterializerUtilities.PopulateComplexObject(row, item, null); //Change tracking objects shouldn't be materialized as unchanged. var tracking = item as IChangeTracking; tracking?.AcceptChanges(); yield return(new KeyValuePair <Row, T>(row, item)); } }
/// <summary> /// Converts the table into an enumeration of objects of the indicated type. /// </summary> /// <typeparam name="T">Desired object type</typeparam> public IEnumerable <T> ToObjects <T>() where T : new() { foreach (var row in Rows) { var item = new T(); MaterializerUtilities.PopulateComplexObject(row, item, null); //Change tracking objects shouldn't be materialized as unchanged. var tracking = item as IChangeTracking; tracking?.AcceptChanges(); yield return(item); } }
public MasterDetailCollectionMaterializer(DbCommandBuilder <TCommand, TParameter> commandBuilder, string masterKeyColumn, Func <TMaster, ICollection <TDetail> > map, CollectionOptions masterOptions, CollectionOptions detailOptions) : base(commandBuilder) { m_MasterKeyColumn = masterKeyColumn; m_Map = map; if (masterOptions.HasFlag(CollectionOptions.InferConstructor)) { m_MasterConstructor = MaterializerUtilities.InferConstructor(s_MasterMetadata); } if (detailOptions.HasFlag(CollectionOptions.InferConstructor)) { m_DetailConstructor = MaterializerUtilities.InferConstructor(s_DetailMetadata); } }
TObject?ConstructObject(IReadOnlyDictionary <string, object?>?row, int?rowCount) { if (rowCount == 0 || row == null) { if (!m_RowOptions.HasFlag(RowOptions.PreventEmptyResults)) { return(null); } else { throw new MissingDataException($"No rows were returned and {nameof(RowOptions)}.{nameof(RowOptions.PreventEmptyResults)} was enabled."); } } else if (rowCount > 1 && !m_RowOptions.HasFlag(RowOptions.DiscardExtraRows)) { throw new UnexpectedDataException($"Expected 1 row but received {rowCount} rows. If this was expected, use `RowOptions.DiscardExtraRows`."); } return(MaterializerUtilities.ConstructObject <TObject>(row, Constructor, Converter)); }
/// <summary> /// Materializers use this to pick a constructor. /// </summary> /// <returns></returns> /// <exception cref="MappingException"></exception> protected ConstructorMetadata InferConstructor() { return(MaterializerUtilities.InferConstructor(ObjectMetadata)); }
public override IReadOnlyList <string> DesiredColumns() { //We need to pick the constructor now so that we have the right columns in the SQL. //If we wait until materialization, we could have missing or extra columns. if (m_MasterConstructor == null && !s_MasterMetadata.Constructors.HasDefaultConstructor) { m_MasterConstructor = MaterializerUtilities.InferConstructor(s_MasterMetadata); } if (m_DetailConstructor == null && !s_DetailMetadata.Constructors.HasDefaultConstructor) { m_DetailConstructor = MaterializerUtilities.InferConstructor(s_DetailMetadata); } var masterColumns = (m_MasterConstructor == null) ? s_MasterMetadata.ColumnsFor : m_MasterConstructor.ParameterNames; var detailColumns = (m_DetailConstructor == null) ? s_DetailMetadata.ColumnsFor : m_DetailConstructor.ParameterNames; //Sanity checks if (m_IncludedColumns != null && m_ExcludedColumns != null) { throw new InvalidOperationException("Cannot specify both included and excluded columns/properties."); } if (m_ExcludedColumns != null && (m_MasterConstructor != null || m_DetailConstructor != null)) { throw new InvalidOperationException("Cannot specify excluded columns/properties with non-default constructors."); } if (masterColumns.Length == 0) { throw new MappingException($"Type {typeof(TMaster).Name} has no writable properties. Please use the InferConstructor option or the WithMasterConstructor method."); } if (detailColumns.Length == 0) { throw new MappingException($"Type {typeof(TDetail).Name} has no writable properties. Please use the InferConstructor option or the WithMasterConstructor method."); } //Assembly the list var columnNames = new HashSet <string>(); //using this to filter out duplicates if (m_IncludedColumns != null) { columnNames.AddRange(m_IncludedColumns); } else //Use the previously found values { columnNames.AddRange(masterColumns); columnNames.AddRange(detailColumns); if (m_ExcludedColumns != null) { foreach (var column in m_ExcludedColumns) { columnNames.Remove(column); } } } columnNames.Add(m_MasterKeyColumn); //Force this to always be included. return(columnNames.ToImmutableArray()); }
List <TMaster> GenerateObjects(Table table) { IEqualityComparer <object> comparer; var columnType = table.ColumnTypeMap[m_MasterKeyColumn]; if (columnType == typeof(short)) { comparer = new Int16EqualityComparer(); } else if (columnType == typeof(int)) { comparer = new Int32EqualityComparer(); } else if (columnType == typeof(long)) { comparer = new Int64EqualityComparer(); } else if (columnType == typeof(Guid)) { comparer = new GuidEqualityComparer(); } else if (columnType == typeof(string)) { comparer = new StringEqualityComparer(); } else if (columnType == typeof(DateTime)) { comparer = new DateTimeEqualityComparer(); } else if (columnType == typeof(DateTimeOffset)) { comparer = new DateTimeOffsetEqualityComparer(); } else if (columnType == typeof(ulong)) { comparer = new UInt64EqualityComparer(); } else { throw new NotSupportedException($"Key column of type '{columnType.Name}' is not supported for Master/Detail collections."); } var groups = new Dictionary <object, List <Row> >(comparer); foreach (var row in table.Rows) { var key = row[m_MasterKeyColumn]; if (key == null) { throw new MissingDataException($"A null was found in the master key column '{m_MasterKeyColumn}'"); } if (!groups.TryGetValue(key, out var group)) { group = new(); groups.Add(key, group); } group.Add(row); } var result = new List <TMaster>(); foreach (var group in groups.Values) { var master = MaterializerUtilities.ConstructObject <TMaster>(group[0], m_MasterConstructor, Converter); var target = m_Map(master); foreach (var row in group) { target.Add(MaterializerUtilities.ConstructObject <TDetail>(row, m_DetailConstructor, Converter)); } result.Add(master); } return(result); }