예제 #1
0
 public string ToSQLString(string Quotes, string paramPrefix, List <XSqlCommandParam> Params, ISqlCompiler compiler)
 {
     if (Params == null)
     {
         return(Name + "(" + string.Join(",", Arguments.Select(p => p.ToSQLString(Quotes, paramPrefix, Params, compiler))) + ")");
     }
     else
     {
         return(compiler.Compile(this.Clone(), Params));
     }
     throw new NotImplementedException();
 }
예제 #2
0
        /// <summary>
        /// Takes a <see cref="IResolveFieldContext"/> and returns a hydrated object with the data.
        /// </summary>
        /// <param name="context">The <see cref="IResolveFieldContext"/>.</param>
        /// <param name="databaseCall">A <see cref="DatabaseCallDelegate"/> that is passed the compiled SQL and calls the database and returns a <see cref="IDataReader"/>.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
        /// <returns>The correctly nested data from the database.</returns>
        /// <exception cref="ArgumentNullException">If <c>context</c> or <c>databaseCall</c> is null.</exception>
        public async Task <object?> ExecuteAsync(IResolveFieldContext context, DatabaseCallDelegate databaseCall,
                                                 CancellationToken cancellationToken)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (databaseCall == null)
            {
                throw new ArgumentNullException(nameof(databaseCall));
            }

            var sqlAst    = _converter.Convert(context);
            var sqlResult = _compiler.Compile(sqlAst, context);

            var data = new List <Dictionary <string, object?> >();

            using (var reader = await databaseCall(sqlResult.Sql, sqlResult.Parameters).ConfigureAwait(false))
            {
                while (await reader.ReadAsync(cancellationToken))
                {
                    var item = new Dictionary <string, object?>();

                    for (var i = 0; i < reader.FieldCount; ++i)
                    {
                        var value = await reader.IsDBNullAsync(i, cancellationToken)
                            ? null
                            : await reader.GetFieldValueAsync <object>(i, cancellationToken);

                        item[reader.GetName(i)] = value;
                    }

                    data.Add(item);
                }
            }

            var objectShape = _objectShaper.DefineObjectShape(sqlAst);

#pragma warning disable 8620
            var nested = _hydrator.Nest(data, objectShape);
            var result = _arrayToConnectionConverter.Convert(nested, sqlAst, context);
#pragma warning restore 8620

            if (result == null)
            {
                return(null);
            }

            await _batchPlanner.NextBatch(sqlAst, result, databaseCall, context, cancellationToken).ConfigureAwait(false);

            return(result);
        }
예제 #3
0
        private async Task NextBatchChild(SqlTable sqlTable, object?data, DatabaseCallDelegate databaseCall,
                                          IResolveFieldContext context, CancellationToken cancellationToken)
        {
            var fieldName = sqlTable.FieldName;

            data = data switch
            {
                List <object> objList => objList.Select(x => (IDictionary <string, object?>)x),
                Connection <object> connection => connection.Items.Select(x => (IDictionary <string, object?>)x),
                _ => data
            };

            // see if any begin a new batch
            if (sqlTable.Batch != null || sqlTable.Junction?.Batch != null)
            {
                string thisKey   = null !;
                string parentKey = null !;

                if (sqlTable.Batch != null)
                {
                    // if so, we know we'll need to get the key for matching with the parent key
                    sqlTable.AddColumn(sqlTable.Batch.ThisKey);

                    thisKey   = sqlTable.Batch.ThisKey.FieldName;
                    parentKey = sqlTable.Batch.ParentKey.FieldName;
                }
                else if (sqlTable.Junction?.Batch != null)
                {
                    sqlTable.AddColumn(sqlTable.Junction.Batch.ThisKey);

                    thisKey   = sqlTable.Junction.Batch.ThisKey.FieldName;
                    parentKey = sqlTable.Junction.Batch.ParentKey.FieldName;
                }

                if (data is IEnumerable <IDictionary <string, object?> > entries)
                {
                    // the "batch scope" is the set of values to match this key against from the previous batch
                    var batchScope = new List <object>();
                    var entryList  = entries.ToList();

                    foreach (var entry in entryList)
                    {
                        var values = PrepareValues(entry, parentKey);
                        batchScope.AddRange(values);
                    }

                    if (batchScope.Count == 0)
                    {
                        return;
                    }

                    // generate the SQL, with the batch scope values incorporated in a WHERE IN clause
                    var sqlResult   = _compiler.Compile(sqlTable, context, SqlDialect.CastArray(batchScope));
                    var objectShape = _objectShaper.DefineObjectShape(sqlTable);

                    // grab the data
                    var newData = await HandleDatabaseCall(databaseCall, sqlResult, objectShape, cancellationToken);

                    // group the rows by the key so we can match them with the previous batch
                    var newDataGrouped = newData.GroupBy(x => x[thisKey])
                                         .ToDictionary(x => x.Key, x => x.ToList());

                    // but if we paginate, we must convert to connection type first
                    if (sqlTable.Paginate)
                    {
                        //TODO: implement
                        // foreach (var group in newDataGrouped)
                        //     newDataGrouped[group.Key] =
                        //         (List<Dictionary<string, object?>>)
                        //             _arrayToConnectionConverter.Convert(group.Value, sqlTable, context);
                    }

                    // if we they want many rows, give them an array
                    if (sqlTable.GrabMany)
                    {
                        foreach (var entry in entryList)
                        {
                            var values = PrepareValues(entry, parentKey);

                            var res = new List <Dictionary <string, object?> >();
                            foreach (var value in values)
                            {
                                if (newDataGrouped.TryGetValue(value, out var obj))
                                {
                                    res.AddRange(obj);
                                }
                            }

                            entry[fieldName] = res;
                        }
                    }
                    else
                    {
                        var matchedData = new List <object>();
                        foreach (var entry in entryList)
                        {
                            if (entry.TryGetValue(parentKey, out var key) == false)
                            {
                                continue;
                            }
                            if (newDataGrouped.TryGetValue(key, out var list) && list.Count > 0)
                            {
                                entry[fieldName] = _arrayToConnectionConverter.Convert(list[0], sqlTable, context);
                                matchedData.Add(entry);
                            }
                            else
                            {
                                entry[fieldName] = null;
                            }
                        }

                        data = matchedData;
                    }


                    switch (data)
                    {
                    case IEnumerable <IDictionary <string, object?> > list:
                    {
                        var nextLevelData = list
                                            .Where(x => x.Count > 0)
                                            .Select(x =>
                            {
                                if (x.TryGetValue(fieldName, out var value) &&
                                    value is List <Dictionary <string, object?> > dict)
                                {
                                    return(dict);
                                }

                                return(null);
                            })
                                            .Where(x => x is { Count: > 0 })
                                            .SelectMany(x => x)
                                            .Select(x => x.ToDictionary())
                                            .ToList();

                        await NextBatch(sqlTable, nextLevelData, databaseCall, context, cancellationToken);

                        return;
                    }