Beispiel #1
0
        private ContainsExpression VisitContains(MethodCallExpression expression)
        {
            // Handle extension methods defined by Linqs
            if (IsDeclaring(expression, typeof(Queryable), typeof(Enumerable)))
            {
                AExpression values = Visit <AExpression>(expression.Arguments[0]);
                AExpression value  = Visit <AExpression>(expression.Arguments[1]);
                return(new ContainsExpression(values, value));
            }

            // Handle custom extension method
            if (IsDeclaring(expression, typeof(SqlQueryableHelper)))
            {
                // Get value expression first, because the source will change to the subquery making the value out of scope
                AExpression value = Visit <AExpression>(expression.Arguments[2]);

                // Evaluate the subquery expressions
                ASourceExpression source      = Visit <ASourceExpression>(expression.Arguments[0]);
                LambdaExpression  fieldLambda = (LambdaExpression)StripQuotes(expression.Arguments[1]);
                FieldExpression   field       = Visit <FieldExpression>(fieldLambda.Body);

                // Create the expression
                return(new ContainsExpression(new ScalarExpression(source, field), value));
            }

            throw new MethodTranslationException(expression.Method);
        }
        private SelectExpression VisitSkip(MethodCallExpression expression)
        {
            if (IsDeclaring(expression, typeof(Queryable), typeof(Enumerable)))
            {
                ASourceExpression source = Visit <ASourceExpression>(expression.Arguments[0]);
                int count = (int)((ConstantExpression)expression.Arguments[1]).Value;
                return(new SelectExpression(source, source.Fields, -1, count));
            }

            throw new MethodTranslationException(expression.Method);
        }
        /// <summary>
        /// Initializes a new instance of <see cref="AggregateExpression"/> with the specified source and function.
        /// </summary>
        /// <param name="source">The source expression to perform the aggregate function on.</param>
        /// <param name="field">The field the aggregate function is applied to.</param>
        /// <param name="value">The type of aggregate operation to perform.</param>
        public AggregateExpression(ASourceExpression source, FieldExpression field, AggregateFunction function)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (field == null)
                throw new ArgumentNullException(nameof(field));

            Source = source;
            SourceField = field;
            Function = function;
        }
        public JoinExpression VisitJoin(MethodCallExpression expression)
        {
            // Handle the default Queryable extension Join
            if (IsDeclaring(expression, typeof(Queryable), typeof(Enumerable)))
            {
                // Resolve the sources
                ASourceExpression outer = Visit <ASourceExpression>(expression.Arguments[0]);
                ASourceExpression inner = Visit <ASourceExpression>(expression.Arguments[1]);

                // Set the active expressions (so fields calls can find their expression)
                sources = new[] { outer, inner };

                // Create the predicate
                LambdaExpression     outerLambda = (LambdaExpression)StripQuotes(expression.Arguments[2]);
                LambdaExpression     innerLambda = (LambdaExpression)StripQuotes(expression.Arguments[3]);
                FieldExpression      outerField  = Visit <FieldExpression>(outerLambda.Body);
                FieldExpression      innerField  = Visit <FieldExpression>(innerLambda.Body);
                APredicateExpression predicate   = new CompositeExpression(outerField, innerField, CompositeOperator.Equal);

                // Decode the result selector
                IEnumerable <FieldExpression> fields = DecodeJoinSelector(expression.Arguments[4], outer.Fields, inner.Fields);

                // Create the expression
                return(new JoinExpression(outer, inner, predicate, fields, JoinType.Inner));
            }

            // Handle the default SqlQueryableHelper extension Join
            if (IsDeclaring(expression, typeof(SqlQueryableHelper)))
            {
                // Resolve the sources
                ASourceExpression outer = Visit <ASourceExpression>(expression.Arguments[0]);
                ASourceExpression inner = Visit <ASourceExpression>(expression.Arguments[1]);

                // Set the active expressions (so fields calls can find their expression)
                sources = new[] { outer, inner };

                // Create the predicate
                LambdaExpression     predicateLambda = (LambdaExpression)StripQuotes(expression.Arguments[2]);
                APredicateExpression predicate       = Visit <APredicateExpression>(predicateLambda.Body);

                // Decode the result selector
                IEnumerable <FieldExpression> fields = DecodeJoinSelector(expression.Arguments[3], outer.Fields, inner.Fields);

                // Resolve the join type
                ConstantExpression joinType = (ConstantExpression)expression.Arguments[4];

                // Create the expression
                return(new JoinExpression(outer, inner, predicate, fields, (JoinType)joinType.Value));
            }

            throw new MethodTranslationException(expression.Method);
        }
Beispiel #5
0
        private WhereExpression VisitWhere(MethodCallExpression expression)
        {
            // Handle the default Queryable extension Where
            if (IsDeclaring(expression, typeof(Queryable), typeof(Enumerable)))
            {
                ASourceExpression    source    = Visit <ASourceExpression>(expression.Arguments[0]);
                LambdaExpression     lambda    = (LambdaExpression)StripQuotes(expression.Arguments[1]);
                APredicateExpression predicate = Visit <APredicateExpression>(lambda.Body);
                return(new WhereExpression(source, predicate));
            }

            throw new MethodTranslationException(expression.Method);
        }
 /// <summary>
 /// Creates a unique name for an <see cref="IQueryNode"/>. Identical input nodes that have the same reference will always produce the same name.
 /// </summary>
 /// <param name="node">The input node to get an identifiable name for.</param>
 /// <returns>A unique name for the specified <see cref="IQueryNode"/> in the format "t{index}".</returns>
 public string GetSource(ASourceExpression node)
 {
     if (sources.TryGetValue(node, out string name))
     {
         return(name);
     }
     else
     {
         name          = "t" + source++;
         sources[node] = name;
         return(name);
     }
 }
Beispiel #7
0
        private SelectExpression VisitOrderBy(MethodCallExpression expression)
        {
            MethodInfo method = expression.Method;

            if (IsDeclaring(expression, typeof(Queryable), typeof(Enumerable), typeof(SqlQueryableHelper)))
            {
                // Resolve the source
                ASourceExpression source = Visit <ASourceExpression>(expression.Arguments[0]);

                // Resolve the optional selector
                FieldExpression field = source.Fields.First();
                if (expression.Arguments.Count > 1)
                {
                    LambdaExpression lambda = (LambdaExpression)StripQuotes(expression.Arguments[1]);
                    field = Visit <FieldExpression>(lambda.Body);
                }

                // Decode the direction
                OrderType direction = method.Name.EndsWith("Descending") ? OrderType.Descending : OrderType.Ascending;

                // Handle an existing select expression
                if (source is SelectExpression select)
                {
                    if (method.Name.StartsWith("ThenBy") && !select.Orderings.Any())
                    {
                        throw new InvalidOperationException($"{method.Name} can only be applied to an ordered sequence.");
                    }
                    if (method.Name.StartsWith("OrderBy") && select.Orderings.Any())
                    {
                        throw new InvalidOperationException($"{method.Name} can only be applied to an unordered sequence.");
                    }

                    // Clone and modify the select expression
                    IEnumerable <FieldExpression> fields    = select.Fields.Select(x => x.SourceExpression);
                    IEnumerable <Ordering>        orderings = select.Orderings.Concat(new[] { new Ordering(field.SourceExpression, direction) });
                    return(new SelectExpression(select.Source, fields, select.Take, select.Skip, orderings));
                }

                // Create the expression
                if (method.Name.StartsWith("ThenBy"))
                {
                    throw new InvalidOperationException($"{method.Name} can only be applied to an ordered sequence.");
                }
                return(new SelectExpression(source, orderings: new[] { new Ordering(field, direction) }));
            }

            throw new MethodTranslationException(expression.Method);
        }
        /// <summary>
        /// Initializes a new instance of <see cref="WhereExpression"/> filtering the specified source.
        /// </summary>
        /// <param name="source">The source expression to select from.</param>
        public WhereExpression(ASourceExpression source, APredicateExpression predicate)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (predicate == null)
            {
                throw new ArgumentNullException(nameof(predicate));
            }

            Source    = source;
            Predicate = predicate;

            Fields      = new FieldExpressions(this, source.Fields);
            Expressions = new[] { source };
        }
        /// <summary>
        /// Initializes a new instance of <see cref="TableExpression"/>, selecting the specified fields from the specified table.
        /// </summary>
        /// <param name="table">The name of the table in the database to query.</param>
        /// <param name="alias">The alias name the <see cref="TableExpression"/> should expose for other queries.</param>
        public TableExpression(string table, string alias, IEnumerable <string> fields)
        {
            if (string.IsNullOrWhiteSpace(table))
            {
                throw new ArgumentException("Cannot be whitespace.", nameof(table));
            }
            if (string.IsNullOrWhiteSpace(alias))
            {
                throw new ArgumentException("Cannot be whitespace.", nameof(alias));
            }
            if (fields == null)
            {
                throw new ArgumentNullException(nameof(fields));
            }

            Table       = table;
            Alias       = alias;
            Fields      = new FieldExpressions(this, alias, fields);
            Expressions = new ASourceExpression[0];
        }
        /// <summary>
        /// Initializes a new instance of <see cref="JoinExpression"/> with the specified sources.
        /// </summary>
        /// <param name="outer">The outer source expression to aggregate.</param>
        /// <param name="inner">The inner source expression to aggregate.</param>
        /// <param name="predicate">The optional predicate to condition the join on.</param>
        /// <param name="fields">The fields to select from the sources. If null, all fields are selected.</param>
        /// <param name="joinType">The type of join to perform.</param>
        public JoinExpression(ASourceExpression outer, ASourceExpression inner, APredicateExpression predicate = null, IEnumerable <FieldExpression> fields = null, JoinType joinType = JoinType.Inner)
        {
            if (outer == null)
            {
                throw new ArgumentNullException(nameof(outer));
            }
            if (inner == null)
            {
                throw new ArgumentNullException(nameof(inner));
            }

            Outer     = outer;
            Inner     = inner;
            Predicate = predicate ?? new BooleanExpression(true);
            JoinType  = joinType;

            fields      = fields ?? outer.Fields.Concat(inner.Fields);
            Fields      = new FieldExpressions(this, fields);
            Expressions = new[] { outer, inner };
        }
        /// <summary>
        /// Initializes a new instance of <see cref="FieldExpressions"/> with the specified source and fields.
        /// </summary>
        /// <param name="source">The expression which the fields belong to.</param>
        /// <param name="fields">The fields to add.</param>
        public FieldExpressions(ASourceExpression source, IEnumerable <FieldExpression> fields)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (fields == null)
            {
                throw new ArgumentNullException(nameof(fields));
            }
            if (fields.Any(x => x == null))
            {
                throw new ArgumentException("One or more field was null.", nameof(fields));
            }

            foreach (FieldExpression field in fields)
            {
                Add(new FieldExpression(source, field.TableName, field.FieldName, field));
            }
        }
Beispiel #12
0
        /// <summary>
        /// Initializes a new instance of <see cref="SelectExpression"/> selecting the specified fields from the source.
        /// </summary>
        /// <param name="source">The source expression to select from.</param>
        /// <param name="fields">The fields to select from the source.</param>
        /// <param name="take">The number of fields to take from the source. Values less than zero indicate all rows.</param>
        /// <param name="skip">The number of fields to ignore on the source before reading rows.</param>
        /// <param name="orderings">The optional sorting of the selected rows.</param>
        /// <exception cref="ArgumentNullException">fields is null.</exception>
        /// <exception cref="ArgumentException">fields contains no elements or orderings contains duplicate field orderings.</exception>
        /// <exception cref="KeyNotFoundException">An ordering key could not be found in fields.</exception>
        public SelectExpression(ASourceExpression source, IEnumerable <FieldExpression> fields, int take = -1, int skip = 0, IEnumerable <Ordering> orderings = null)
        {
            if (fields == null)
            {
                throw new ArgumentNullException(nameof(fields));
            }
            if (!fields.Any())
            {
                throw new ArgumentException("There must be at least one field specified in a select query.", nameof(fields));
            }

            Source      = source;
            Fields      = new FieldExpressions(this, fields);
            Expressions = new[] { source };
            Take        = take;
            Skip        = skip;

            Orderings = orderings?
                        .Select(x =>
            {
                if (x == null)
                {
                    throw new ArgumentException("None of the ordering can be null.", nameof(orderings));
                }

                int count = orderings.Count(y => y.Field == x.Field);
                if (count != 1)
                {
                    throw new ArgumentException($"One ordering per field is allowed but '[{x.Field.TableName}].[{x.Field.FieldName}]' has {count}.", nameof(orderings));
                }
                if (!Fields.Any(y => y.SourceExpression == x.Field))
                {
                    throw new KeyNotFoundException($"The ordering field '[{x.Field.TableName}].[{x.Field.FieldName}]' could not be found in the fields parameter.");
                }

                return(x);
            })
                        .ToArray();
        }
Beispiel #13
0
        private AggregateExpression VisitCount(MethodCallExpression expression)
        {
            if (IsDeclaring(expression, typeof(Queryable), typeof(Enumerable), typeof(SqlQueryableHelper)))
            {
                // Map the source
                ASourceExpression source = Visit <ASourceExpression>(expression.Arguments[0]);

                // Resolve the optional predicate
                if (expression.Arguments.Count > 1)
                {
                    LambdaExpression     lambda    = (LambdaExpression)StripQuotes(expression.Arguments[1]);
                    APredicateExpression predicate = Visit <APredicateExpression>(lambda.Body);
                    source = new WhereExpression(source, predicate);
                }

                // Resolve the field to be counted (must be done after the source has been manipulated)
                FieldExpression field = source.Fields.First();

                // Create the expression
                return(new AggregateExpression(source, field, AggregateFunction.Count));
            }

            throw new MethodTranslationException(expression.Method);
        }
        /// <summary>
        /// Initializes a new instance of <see cref="FieldExpressions"/> with the specified table and fields names.
        /// </summary>
        /// <param name="source">The expression which the fields belong to.</param>
        /// <param name="table">The name of the table all the fields are in.</param>
        /// <param name="fields">The fields to add.</param>
        public FieldExpressions(ASourceExpression source, string table, IEnumerable <string> fields)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (string.IsNullOrWhiteSpace(table))
            {
                throw new ArgumentException("Cannot be null or whitespace.", nameof(table));
            }
            if (fields == null)
            {
                throw new ArgumentNullException(nameof(fields));
            }
            if (fields.Any(x => string.IsNullOrWhiteSpace(x)))
            {
                throw new ArgumentException("One or more field was whitespace.", nameof(fields));
            }

            foreach (string field in fields)
            {
                Add(new FieldExpression(source, table, field));
            }
        }
Beispiel #15
0
 /// <summary>
 /// Initializes a new instance of <see cref="SelectExpression"/> selecting all the fields from the specified source.
 /// </summary>
 /// <param name="source">The source expression to select from.</param>
 /// <param name="take">The number of fields to take from the source. Values less than zero indicate all rows.</param>
 /// <param name="skip">The number of fields to ignore on the source before reading rows.</param>
 /// <param name="orderings">The optional sorting of the selected rows.</param>
 /// <exception cref="ArgumentNullException">the source <see cref="ASourceExpression.Fields"/> property is null.</exception>
 /// <exception cref="ArgumentException">the source <see cref="ASourceExpression.Fields" /> property contains no elements.</exception>
 /// <exception cref="KeyNotFoundException">An ordering key could not be found in the sources <see cref="ASourceExpression.Fields"/> property.</exception>
 public SelectExpression(ASourceExpression source, int take = -1, int skip = 0, IEnumerable <Ordering> orderings = null)
     : this(source, source?.Fields, take, skip, orderings)
 {
 }
Beispiel #16
0
 /// <summary>
 /// Initializes a new instance of <see cref="ScalarExpression"/> selecting the single field exposed on the source.
 /// </summary>
 /// <param name="source">The source expression to select from.</param>
 public ScalarExpression(ASourceExpression source)
     : this(source, source?.Fields.Single())
 {
 }
Beispiel #17
0
 /// <summary>
 /// Initializes a new instance of <see cref="ScalarExpression"/> selecting the specified field from the source.
 /// </summary>
 /// <param name="source">The source expression to select from.</param>
 /// <param name="field">The field to select from the source.</param>
 public ScalarExpression(ASourceExpression source, FieldExpression field)
     : base(source, field != null ? new[] { field } : throw new ArgumentNullException(nameof(field)))
 {
 }
        public static IEnumerable <Record> ExecuteQuery(this DbConnection connection, ASourceExpression expression, SqlQueryVisitor visitor)
        {
            // Create the outer select expression
            SelectExpression select = expression as SelectExpression;

            if (select == null)
            {
                select = new SelectExpression(expression, expression.Fields);
            }

            // Ensure the connection is open
            if (connection.State != ConnectionState.Open)
            {
                throw new InvalidOperationException("The connection must be open to execute a query.");
            }

            // Create the command
            LinkedList <Record> items = new LinkedList <Record>();

            using (DbCommand command = connection.CreateCommand())
            {
                // Prepare the query
                Query query = visitor.GenerateQuery(select);
                command.CommandText = query.Sql;
                foreach (KeyValuePair <string, object> pair in query.Parameters)
                {
                    command.AddParameter(pair.Key, pair.Value);
                }

                // Execute the query
                using (DbDataReader reader = command.ExecuteReader())
                {
                    ILookup <string, CommandField> fields = reader.GetFieldMap(select.Fields);
                    while (reader.Read())
                    {
                        // Read the row of the result set
                        Dictionary <string, RecordItem> row = fields
                                                              .Select(x => ReadItem(reader, x, x.Key))
                                                              .ToDictionary(x => x.Key);

                        // At this point the row has been created
                        items.AddLast(new Record(row));
                    }
                }
            }
            return(items);
        }