예제 #1
0
    /// <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);
    }
예제 #2
0
    /// <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);
    }
예제 #3
0
    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);
    }
예제 #4
0
 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));
 }
예제 #5
0
        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));
            }
        }
예제 #6
0
        /// <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);
            }
        }
예제 #7
0
    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);
        }
    }
예제 #8
0
 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));
 }
예제 #9
0
 /// <summary>
 /// Materializers use this to pick a constructor.
 /// </summary>
 /// <returns></returns>
 /// <exception cref="MappingException"></exception>
 protected ConstructorMetadata InferConstructor()
 {
     return(MaterializerUtilities.InferConstructor(ObjectMetadata));
 }
예제 #10
0
    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());
    }
예제 #11
0
    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);
    }