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); }
public object Execute(Expression expression) { // Translate the expression tree expression = LinqTranslatorVisitor.Translate(expression); // Covert prediate (scalar) expressions to a source query if (expression is APredicateExpression predicate) { FieldExpression value = new FieldExpression(predicate, "Scalar", "Value"); expression = new ScalarExpression(null, value); } // Covert aggregate (scalar) expressions to a source query if (expression is AggregateExpression aggregate) { FieldExpression value = new FieldExpression(aggregate, "Scalar", "Value"); expression = new ScalarExpression(aggregate.Source, value); } // Execute the query if (expression is ASourceExpression source) { return(connection.ExecuteQuery(source, visitor)); } throw new NotSupportedException("The expression could not be converted to sql."); }
/// <summary> /// Initializes a new instance of <see cref="Ordering"/> with the specified field and order direction. /// </summary> /// <param name="field">The field to order.</param> /// <param name="type">The direction to order the column.</param> public Ordering(FieldExpression field, OrderType type) { if (field == null) { throw new ArgumentNullException(nameof(field)); } Field = field; OrderType = type; }
/// <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); }
/// <summary> /// Visits the specified expression. /// </summary> /// <param name="expression">The expression to visit.</param> public virtual Expression VisitField(FieldExpression expression) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (Context.Source == null) { throw new InvalidOperationException($"A field cannot be visited unless an {nameof(ASourceExpression)} has been visited."); } if (!(expression.ValueExpression is ASourceExpression sourceValueExpression)) { throw new InvalidOperationException($"A field exposed from a source expression must itself have an {nameof(ASourceExpression)}. Did you mean to call VisitFieldDeclaration?"); }
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="FieldExpression"/>, selecting the specified field from the specified source. /// </summary> /// <param name="valueExpression">The fields <see cref="AExpression"/> representing the value of the field.</param> /// <param name="table">The table or table alias the field is exposed from.</param> /// <param name="field">The name of the field on the source.</param> /// <param name="sourceExpression">The optional field expression this instance is mapping.</param> public FieldExpression(AExpression valueExpression, string table, string field, FieldExpression sourceExpression = null) { if (valueExpression == null) { throw new ArgumentNullException(nameof(valueExpression)); } if (string.IsNullOrWhiteSpace(table)) { throw new ArgumentException("Cannot be whitespace.", nameof(table)); } if (string.IsNullOrWhiteSpace(field)) { throw new ArgumentException("Cannot be whitespace.", nameof(field)); } ValueExpression = valueExpression; TableName = table; FieldName = field; SourceExpression = sourceExpression; }
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); }
private FieldExpression VisitField(MethodCallExpression expression) { if (sources == null) { throw new InvalidOperationException($"A field can only be visited if an {nameof(ASourceExpression)} has previously been visited."); } // Resolve the field name ConstantExpression fieldNameExpression = expression.Arguments.FirstOrDefault() as ConstantExpression; if (!expression.Method.DeclaringType.IsAssignableFrom(typeof(Dictionary <string, object>))) { throw new InvalidOperationException("The declaring type for a field expression must be a Dictionary<string, object>."); } if (expression.Method.ReturnType != typeof(object)) { throw new InvalidOperationException("The return type for a field expression must be type of object."); } if (expression.Arguments.Count != 1 || fieldNameExpression?.Type != typeof(string)) { throw new InvalidOperationException("The field name indexer for the field expression must contain exactly one string parameter."); } // Resolve the table name MethodCallExpression source = expression.Object as MethodCallExpression; ConstantExpression tableNameExpression = source?.Arguments.FirstOrDefault() as ConstantExpression; if (source == null) { throw new InvalidOperationException($"The table instance object could not be resolved for the field: {fieldNameExpression.Value.ToString()}"); } if (source.Method.Name != "get_Item") { throw new NotSupportedException("Only an array indexer can be used to resolve a fields table name."); } if (!source.Method.ReturnType.IsAssignableFrom(typeof(RecordItem))) { throw new InvalidOperationException($"When mapping a field, the table name must map to a {nameof(RecordItem)}."); } if (source.Arguments.Count != 1 || tableNameExpression?.Type != typeof(string)) { throw new InvalidOperationException("The table name indexer for the field expression must contain exactly one string parameter."); } // Resolve the indexer values string tableName = tableNameExpression.Value as string; string fieldName = fieldNameExpression.Value as string; if (string.IsNullOrWhiteSpace(tableName)) { throw new InvalidOperationException("The table name cannot be empty."); } if (string.IsNullOrWhiteSpace(fieldName)) { throw new InvalidOperationException("The field name cannot be empty."); } // Get the field from the current source FieldExpression found = sources? .SelectMany(x => x.Fields) .FirstOrDefault(x => x.TableName == tableName && x.FieldName == fieldName); if (found == null) { throw new KeyNotFoundException($"The field [{tableName}].[{fieldName}] could not be found on the current source expression."); } return(found); }
/// <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))) { }