protected override Expression VisitMethodCall(MethodCallExpression node) { if (node.Method.DeclaringType == typeof(QueryableExtensions) && node.Method.Name == nameof(QueryableExtensions.SelectOnly)) { // use last operator call only FieldNames = FieldNames ?? (string[])ExpressionsInternalToolkit.GetConstant(node.Arguments[1]); } return(base.VisitMethodCall(node)); }
protected override Expression VisitMethodCall(MethodCallExpression node) { if (node.Object is ParameterExpression) { var arg = node.Arguments.FirstOrDefault() ?? throw new ArgumentException("Invalid argument."); var memberName = ExpressionsInternalToolkit.GetConstant(arg); _sb.Append(memberName); } return(node); }
protected override Expression VisitConstant(ConstantExpression node) { var value = ExpressionsInternalToolkit.GetConstant(node); var valueType = value?.GetType(); var isCollection = (valueType?.GetInterfaces() .Any(x => ( x == typeof(ICollection) || ( x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection <>) ) ) ) ?? false); if (isCollection) { var inSb = new StringBuilder(); inSb.Append("("); var valuesCollection = value as IEnumerable; var enumerator = valuesCollection.GetEnumerator(); var hasItems = enumerator.MoveNext(); while (hasItems) { var pName = _queryParamsStore.Append(enumerator.Current); inSb.Append(pName); var isLast = !enumerator.MoveNext(); if (isLast) { break; } inSb.Append(", "); } inSb.Append(")"); _sb.Append(inSb); } else { var paramName = _queryParamsStore.Append(value); _sb.Append(paramName); } return(node); }
protected override Expression VisitBinary(BinaryExpression node) { switch (node.NodeType) { case ExpressionType.Or: case ExpressionType.OrElse: _sb.Append("("); Visit(node.Left); _sb.Append(" OR "); Visit(node.Right); _sb.Append(")"); break; case ExpressionType.And: case ExpressionType.AndAlso: _sb.Append("("); Visit(node.Left); _sb.Append(" AND "); Visit(node.Right); _sb.Append(")"); break; case ExpressionType.GreaterThan: Visit(node.Left); _sb.Append(" > "); Visit(node.Right); break; case ExpressionType.GreaterThanOrEqual: Visit(node.Left); _sb.Append(" >= "); Visit(node.Right); break; case ExpressionType.LessThan: Visit(node.Left); _sb.Append(" < "); Visit(node.Right); break; case ExpressionType.LessThanOrEqual: Visit(node.Left); _sb.Append(" <= "); Visit(node.Right); break; case ExpressionType.Equal: { if (ExpressionsInternalToolkit.TryGetConstant(node.Left, out var resValueLeft) && resValueLeft == null) { Visit(node.Right); if (_notUnary) { _sb.Append(" IS NULL"); } } else if (ExpressionsInternalToolkit.TryGetConstant(node.Right, out var resValueRight) && resValueRight == null) { Visit(node.Left); _sb.Append(" IS NULL"); } else { Visit(node.Left); _sb.Append(" = "); Visit(node.Right); } } break; case ExpressionType.NotEqual: { object resValueLeft; if (ExpressionsInternalToolkit.TryGetConstant(node.Left, out resValueLeft) && resValueLeft == null) { Visit(node.Right); if (_notUnary) { _sb.Append(" IS NOT NULL"); } } else if (ExpressionsInternalToolkit.TryGetConstant(node.Right, out var resValueRight) && resValueRight == null) { Visit(node.Left); _sb.Append(" IS NOT NULL"); } else { Visit(node.Left); _sb.Append(" <> "); Visit(node.Right); } } break; } return(node); }
protected override Expression VisitMethodCall(MethodCallExpression node) { // it is a Enumerable.Contains extension if (node.Method.DeclaringType == typeof(Enumerable) && node.Method.Name == nameof(Enumerable.Contains)) { if (node.Arguments.Count != 2) { throw new InvalidOperationException("Constains call should be with 2 parameter."); } Visit(node.Arguments[1]); _sb.Append(_notUnary ? " NOT IN " : " IN "); var collection = ExpressionsInternalToolkit.GetConstant(node.Arguments[0]); Visit(Expression.Constant(collection)); return(node); } // it is row indexer if (node.Method.DeclaringType == typeof(ShadowRow) && node.Method.Name == "get_Item") { if (node.Arguments.Count != 1) { throw new InvalidOperationException("Only 1 indexer are supported."); } var member = ExpressionsInternalToolkit.GetConstant(node.Arguments[0]); _sb.Append(" "); _sb.Append(member); _sb.Append(" "); return(node); } // it is a string manipulation methods if (node.Method.DeclaringType == typeof(string)) { Visit(node.Object); if (node.Arguments.Count() != 1) { throw new InvalidOperationException("Argument count is invalid."); } var value = ExpressionsInternalToolkit.GetConstant(node.Arguments[0]); if (value?.GetType() != typeof(string)) { throw new InvalidOperationException("Invalid value type."); } if (_notUnary) { _sb.Append(" NOT"); } if (node.Method.Name == nameof(String.Contains)) { var paramName = _queryParamsStore.Append($"%{value}%"); _sb.Append($" LIKE {paramName}"); } else if (node.Method.Name == nameof(String.EndsWith)) { var paramName = _queryParamsStore.Append($"%{value}"); _sb.Append($" LIKE {paramName}"); } else if (node.Method.Name == nameof(String.StartsWith)) { var paramName = _queryParamsStore.Append($"{value}%"); _sb.Append($" LIKE {paramName}"); } else { throw new Exception(); } return(node); } if (node.Method.Name == nameof(Boolean.Equals)) { Visit(node.Object); var value = ExpressionsInternalToolkit.GetConstant(node.Arguments[0]); if (value?.GetType() == null) { _sb.Append(_notUnary ? " IS NOT NULL" : " IS NULL "); } else if (value.GetType() == typeof(bool)) { var boolSqlValue = (bool)value ? 1 : 0; _sb.Append(_notUnary ? " != " : " = "); _sb.Append(_queryParamsStore.Append(boolSqlValue)); } else { _sb.Append(_notUnary ? " != " : " = "); _sb.Append(_queryParamsStore.Append(value)); } } return(node); }
/// <summary> /// Executes the specified query. /// </summary> /// <typeparam name="TResult">The type of the result.</typeparam> /// <param name="expression">The expression.</param> /// <returns></returns> public override TResult Execute <TResult>(Expression expression) { // it is a Count aggregation call if (ExpressionsInternalToolkit.IsCountQueryableCall(expression)) { using (var cmd = PrepareSqlAndParams(expression)) { var val = cmd.ExecuteScalar(); var skipCount = ExpressionsInternalToolkit.GetSkipCount(expression); var countViaSkipAdjusted = Math.Max((int)val - skipCount, 0); if (ExpressionsInternalToolkit.TakeIsUsed(expression)) { var takeCount = ExpressionsInternalToolkit.GetTakeCount(expression); var countViaTakeAdjusted = Math.Min(countViaSkipAdjusted, takeCount); return((TResult)(object)countViaTakeAdjusted); } return((TResult)(object)Math.Max((int)val - skipCount, 0)); } } // otherwise is applied a Query using (var cmd = PrepareSqlAndParams(expression)) { if (ExpressionsInternalToolkit.IsListAsyncCall(expression)) { var ct = ExpressionsInternalToolkit.GetCancellationTokenForToList(expression); return((TResult)(object)cmd.ReadAllAsync(ct)); } else { var collection = new List <ShadowRow>(); using (var enumerator = GetEnumerator <ShadowRow>(expression)) { while (enumerator.MoveNext()) { collection.Add(enumerator.Current); } } var firstAggregationCalled = ExpressionsInternalToolkit.IsFirstQueryableCall(expression); var firstOrDefaultAggregationCalled = ExpressionsInternalToolkit.IsFirstOrDefaultQueryableCall(expression); if (firstAggregationCalled || firstOrDefaultAggregationCalled) { if (collection.Count > 1) { throw new InvalidOperationException("Unexpected collection length."); } if (firstAggregationCalled) { var first = collection.FirstOrDefault(); if (first == null) { throw new InvalidOperationException("Data not found."); } return((TResult)(object)first); } if (firstOrDefaultAggregationCalled) { return((TResult)(object)collection.FirstOrDefault()); } } return((TResult)(object)collection); } } }
public override string TranslateToSql(Expression expr) { var externalPredicates = new List <Expression <Func <ShadowRow, bool> > >(); var itIsCount = false; var itIsFirst = false; var skipUsed = ExpressionsInternalToolkit.SkipIsUsed(expr); var takeUsed = ExpressionsInternalToolkit.TakeIsUsed(expr); #pragma warning disable CS0642 if (itIsCount = ExpressionsInternalToolkit.IsCountQueryableCall(expr)) { ; } else if (itIsFirst = ExpressionsInternalToolkit.IsFirstQueryableCall(expr) || ExpressionsInternalToolkit.IsFirstOrDefaultQueryableCall(expr)) { ; } #pragma warning restore CS0642 var selectOnlyTranslator = new SelectOnlyTranslator(); if (itIsCount) { _sb.Append("SELECT COUNT ("); _sb.Append(selectOnlyTranslator.TranslateToSql(expr)); _sb.Append(") FROM "); var mCall = expr as MethodCallExpression; if (mCall.Arguments.Count == 2) { var lambda = ExpressionsInternalToolkit.SkipUnary(mCall.Arguments[1]) as LambdaExpression; // skip Quote var lambdaTyped = Expression.Lambda <Func <ShadowRow, bool> >(lambda.Body, ExpressionBuilders.DefaultRowParameter); externalPredicates.Add(lambdaTyped); } } else if (itIsFirst && !skipUsed) { _sb.Append("SELECT TOP 1 "); _sb.Append(selectOnlyTranslator.TranslateToSql(expr)); _sb.Append(" FROM "); var mCall = expr as MethodCallExpression; if (mCall.Arguments.Count == 2) { var lambda = ExpressionsInternalToolkit.SkipUnary(mCall.Arguments[1]) as LambdaExpression; // skip Quote var lambdaTyped = Expression.Lambda <Func <ShadowRow, bool> >(lambda.Body, ExpressionBuilders.DefaultRowParameter); externalPredicates.Add(lambdaTyped); } } else { _sb.Append("SELECT "); _sb.Append(selectOnlyTranslator.TranslateToSql(expr)); _sb.Append(" FROM "); } _sb.Append(_sourceName); var whereLine = new WhereQueryTranslator(_queryParamsStore, externalPredicates.Any() ? externalPredicates.ToArray() : null) .TranslateToSql(expr); if (!string.IsNullOrWhiteSpace(whereLine)) { _sb.Append(" WHERE "); _sb.Append(whereLine); } if (!itIsCount) { var orderByLine = new OrderByQueryTranslator(_queryParamsStore) .TranslateToSql(expr); var orderByUsed = !string.IsNullOrWhiteSpace(orderByLine); if (orderByUsed) { _sb.Append(" ORDER BY "); _sb.Append(orderByLine); } if (skipUsed) { if (!orderByUsed) { _sb.Append(" ORDER BY (SELECT NULL)"); } _sb.Append(" OFFSET "); var skipCount = ExpressionsInternalToolkit.GetSkipCount(expr); _sb.Append(_queryParamsStore.Append(skipCount)); _sb.Append(" ROWS"); } if (takeUsed) { if (!skipUsed) { if (!orderByUsed) { _sb.Append(" ORDER BY (SELECT NULL)"); } _sb.Append(" OFFSET 0 ROWS"); } _sb.Append(" FETCH NEXT "); var takeCount = ExpressionsInternalToolkit.GetTakeCount(expr); _sb.Append(_queryParamsStore.Append(takeCount)); _sb.Append(" ROW ONLY"); } } if (itIsFirst && skipUsed) { _sb.Append(" FETCH NEXT 1 ROW ONLY"); } return(_sb.ToString()); }