protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequenceExpr = methodCall.Arguments[0]; var sequence = builder.BuildSequence(new BuildInfo(buildInfo, sequenceExpr)); var groupingType = methodCall.Type.GetGenericArgumentsEx()[0]; var keySelector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); var elementSelector = (LambdaExpression)methodCall.Arguments[2].Unwrap(); if (methodCall.Arguments[0].NodeType == ExpressionType.Call) { var call = (MethodCallExpression)methodCall.Arguments[0]; if (call.Method.Name == "Select") { var type = ((LambdaExpression)call.Arguments[1].Unwrap()).Body.Type; if (type.IsGenericTypeEx() && type.GetGenericTypeDefinition() == typeof(ExpressionBuilder.GroupSubQuery <,>)) { sequence = new SubQueryContext(sequence); } } } var key = new KeyContext(buildInfo.Parent, keySelector, sequence); var groupSql = builder.ConvertExpressions(key, keySelector.Body.Unwrap(), ConvertFlags.Key); if (sequence.SelectQuery.Select.IsDistinct || sequence.SelectQuery.GroupBy.Items.Count > 0 || groupSql.Any(_ => !(_.Sql is SqlField || _.Sql is SqlColumn))) { sequence = new SubQueryContext(sequence); key = new KeyContext(buildInfo.Parent, keySelector, sequence); groupSql = builder.ConvertExpressions(key, keySelector.Body.Unwrap(), ConvertFlags.Key); } foreach (var sql in groupSql) { sequence.SelectQuery.GroupBy.Expr(sql.Sql); } // new QueryVisitor().Visit(sequence.SelectQuery.From, e => // { // if (e.ElementType == QueryElementType.JoinedTable) // { // var jt = (SelectQuery.JoinedTable)e; // if (jt.JoinType == SelectQuery.JoinType.Inner) // jt.IsWeak = false; // } // }); var element = new SelectContext(buildInfo.Parent, elementSelector, sequence /*, key*/); var groupBy = new GroupByContext(buildInfo.Parent, sequenceExpr, groupingType, sequence, key, element); Debug.WriteLine("BuildMethodCall GroupBy:\n" + groupBy.SelectQuery); return(groupBy); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); if (sequence.SelectQuery.Select.TakeValue != null || sequence.SelectQuery.Select.SkipValue != null || sequence.SelectQuery.Select.IsDistinct && !builder.DataContextInfo.SqlProviderFlags.IsDistinctOrderBySupported) { sequence = new SubQueryContext(sequence); } var lambda = (LambdaExpression)methodCall.Arguments[1].Unwrap(); var sparent = sequence.Parent; var order = new ExpressionContext(buildInfo.Parent, sequence, lambda); var body = lambda.Body.Unwrap(); var sql = builder.ConvertExpressions(order, body, ConvertFlags.Key); builder.ReplaceParent(order, sparent); if (!methodCall.Method.Name.StartsWith("Then")) { sequence.SelectQuery.OrderBy.Items.Clear(); } foreach (var expr in sql) { var e = builder.ConvertSearchCondition(sequence, expr.Sql); sequence.SelectQuery.OrderBy.Expr(e, methodCall.Method.Name.EndsWith("Descending")); } return(sequence); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var keySelector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); var keyContext = new SelectContext(buildInfo.Parent, keySelector, sequence); var keySql = builder.ConvertExpressions(keyContext, keySelector.Body.Unwrap(), ConvertFlags.All, null); var uniqueKeys = keySql .Select(info => sequence.SelectQuery.Select.Columns[sequence.SelectQuery.Select.Add(info.Sql)]) .ToArray(); sequence.SelectQuery.UniqueKeys.Add(uniqueKeys); return(new SubQueryContext(sequence)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequenceExpr = methodCall.Arguments[0]; var wrapSequence = false; LambdaExpression?groupingKey = null; var groupingKind = GroupingType.Default; if (sequenceExpr.NodeType == ExpressionType.Call) { var call = (MethodCallExpression)methodCall.Arguments[0]; if (call.IsQueryable("Select")) { var selectParam = (LambdaExpression)call.Arguments[1].Unwrap(); var type = selectParam.Body.Type; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ExpressionBuilder.GroupSubQuery <,>)) { wrapSequence = true; var selectParamBody = selectParam.Body.Unwrap(); MethodCallExpression?groupingMethod = null; if (selectParamBody is MemberInitExpression mi) { var assignment = mi.Bindings.OfType <MemberAssignment>().FirstOrDefault(m => m.Member.Name == "Key"); if (assignment?.Expression.NodeType == ExpressionType.Call) { var mc = (MethodCallExpression)assignment.Expression; if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.Rollup, Methods.LinqToDB.GroupBy.Cube, Methods.LinqToDB.GroupBy.GroupingSets)) { groupingMethod = mc; groupingKey = (LambdaExpression)mc.Arguments[0].Unwrap(); if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.Rollup)) { groupingKind = GroupingType.Rollup; } else if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.Cube)) { groupingKind = GroupingType.Cube; } else if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.GroupingSets)) { groupingKind = GroupingType.GroupBySets; } else { throw new InvalidOperationException(); } } } } if (groupingMethod != null && groupingKey != null) { sequenceExpr = sequenceExpr.Replace(groupingMethod, groupingKey.Body.Unwrap()); } } } } var sequence = builder.BuildSequence(new BuildInfo(buildInfo, sequenceExpr)); var keySequence = sequence; var groupingType = methodCall.Type.GetGenericArguments()[0]; var keySelector = (LambdaExpression)methodCall.Arguments[1].Unwrap() !; var elementSelector = (LambdaExpression)methodCall.Arguments[2].Unwrap() !; if (wrapSequence) { sequence = new SubQueryContext(sequence); } sequence = new SubQueryContext(sequence); var key = new KeyContext(buildInfo.Parent, keySelector, sequence); if (groupingKind != GroupingType.GroupBySets) { var groupSql = builder.ConvertExpressions(key, keySelector.Body.Unwrap(), ConvertFlags.Key, null); var allowed = groupSql.Where(s => !QueryHelper.IsConstant(s.Sql)); foreach (var sql in allowed) { sequence.SelectQuery.GroupBy.Expr(sql.Sql); } } else { var goupingSetBody = groupingKey !.Body; var groupingSets = EnumGroupingSets(goupingSetBody).ToArray(); if (groupingSets.Length == 0) { throw new LinqException($"Invalid groping sets expression '{goupingSetBody}'."); } foreach (var groupingSet in groupingSets) { var groupSql = builder.ConvertExpressions(keySequence, groupingSet, ConvertFlags.Key, null); sequence.SelectQuery.GroupBy.Items.Add( new SqlGroupingSet(groupSql.Select(s => keySequence.SelectQuery.Select.AddColumn(s.Sql)))); } } sequence.SelectQuery.GroupBy.GroupingType = groupingKind; var element = new SelectContext(buildInfo.Parent, elementSelector, sequence /*, key*/); var groupBy = new GroupByContext(buildInfo.Parent, sequenceExpr, groupingType, sequence, key, element, builder.IsGroupingGuardDisabled); Debug.WriteLine("BuildMethodCall GroupBy:\n" + groupBy.SelectQuery); return(groupBy); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var wrapped = false; if (sequence.SelectQuery.Select.TakeValue != null || sequence.SelectQuery.Select.SkipValue != null) { sequence = new SubQueryContext(sequence); wrapped = true; } var lambda = (LambdaExpression)methodCall.Arguments[1].Unwrap(); SqlInfo[] sql; while (true) { var sparent = sequence.Parent; var order = new ExpressionContext(buildInfo.Parent, sequence, lambda); var body = lambda.Body.Unwrap(); sql = builder.ConvertExpressions(order, body, ConvertFlags.Key, null); builder.ReplaceParent(order, sparent); if (wrapped) { break; } // handle situation when order by uses complex field var isComplex = false; foreach (var sqlInfo in sql) { // immutable expressions will be removed later // var isImmutable = QueryHelper.IsConstant(sqlInfo.Sql); if (isImmutable) { continue; } // possible we have to extend this list // isComplex = null != new QueryVisitor().Find(sqlInfo.Sql, e => e.ElementType == QueryElementType.SqlQuery); if (isComplex) { break; } } if (!isComplex) { break; } sequence = new SubQueryContext(sequence); wrapped = true; } if (!methodCall.Method.Name.StartsWith("Then") && !Configuration.Linq.DoNotClearOrderBys) { sequence.SelectQuery.OrderBy.Items.Clear(); } foreach (var expr in sql) { // we do not need sorting by immutable values, like "Some", Func("Some"), "Some1" + "Some2". It does nothing for ordering // if (QueryHelper.IsConstant(expr.Sql)) { continue; } var e = builder.ConvertSearchCondition(expr.Sql); sequence.SelectQuery.OrderBy.Expr(e, methodCall.Method.Name.EndsWith("Descending")); } return(sequence); }