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); }
internal static void BuildSetter( ExpressionBuilder builder, BuildInfo buildInfo, LambdaExpression setter, IBuildContext into, List <SelectQuery.SetExpression> items, IBuildContext sequence) { var path = Expression.Parameter(setter.Body.Type, "p"); var ctx = new ExpressionContext(buildInfo.Parent, sequence, setter); if (setter.Body.NodeType == ExpressionType.MemberInit) { var ex = (MemberInitExpression)setter.Body; var p = sequence.Parent; BuildSetter(builder, into, items, ctx, ex, path); builder.ReplaceParent(ctx, p); } else { var sqlInfo = ctx.ConvertToSql(setter.Body, 0, ConvertFlags.All); foreach (var info in sqlInfo) { if (info.Members.Count == 0) { throw new LinqException("Object initializer expected for insert statement."); } if (info.Members.Count != 1) { throw new InvalidOperationException(); } var member = info.Members[0]; var pe = Expression.MakeMemberAccess(path, member); var column = into.ConvertToSql(pe, 1, ConvertFlags.Field); var expr = info.Sql; items.Add(new SelectQuery.SetExpression(column[0].Sql, expr)); } } }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var isGroup = methodCall.Method.Name == "GroupJoin"; var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0], buildInfo.SelectQuery)); var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); var context = new SubQueryContext(outerContext); innerContext = isGroup ? new GroupJoinSubQueryContext(innerContext) : new SubQueryContext(innerContext); var join = isGroup ? innerContext.SelectQuery.WeakLeftJoin() : innerContext.SelectQuery.InnerJoin(); var sql = context.SelectQuery; sql.From.Tables[0].Joins.Add(join.JoinedTable); var selector = (LambdaExpression)methodCall.Arguments[4].Unwrap(); context.SetAlias(selector.Parameters[0].Name); innerContext.SetAlias(selector.Parameters[1].Name); var outerKeyLambda = ((LambdaExpression)methodCall.Arguments[2].Unwrap()); var innerKeyLambda = ((LambdaExpression)methodCall.Arguments[3].Unwrap()); var outerKeySelector = outerKeyLambda.Body.Unwrap(); var innerKeySelector = innerKeyLambda.Body.Unwrap(); var outerParent = context.Parent; var innerParent = innerContext.Parent; var outerKeyContext = new ExpressionContext(buildInfo.Parent, context, outerKeyLambda); var innerKeyContext = new InnerKeyContext(buildInfo.Parent, innerContext, innerKeyLambda); // Make join and where for the counter. // if (outerKeySelector.NodeType == ExpressionType.New) { var new1 = (NewExpression)outerKeySelector; var new2 = (NewExpression)innerKeySelector; for (var i = 0; i < new1.Arguments.Count; i++) { var arg1 = new1.Arguments[i]; var arg2 = new2.Arguments[i]; BuildJoin(builder, join.JoinedTable.Condition, outerKeyContext, arg1, innerKeyContext, arg2); } } else if (outerKeySelector.NodeType == ExpressionType.MemberInit) { var mi1 = (MemberInitExpression)outerKeySelector; var mi2 = (MemberInitExpression)innerKeySelector; for (var i = 0; i < mi1.Bindings.Count; i++) { if (mi1.Bindings[i].Member != mi2.Bindings[i].Member) { throw new LinqException($"List of member inits does not match for entity type '{outerKeySelector.Type}'."); } var arg1 = ((MemberAssignment)mi1.Bindings[i]).Expression; var arg2 = ((MemberAssignment)mi2.Bindings[i]).Expression; BuildJoin(builder, join.JoinedTable.Condition, outerKeyContext, arg1, innerKeyContext, arg2); } } else { BuildJoin(builder, join.JoinedTable.Condition, outerKeyContext, outerKeySelector, innerKeyContext, innerKeySelector); } builder.ReplaceParent(outerKeyContext, outerParent); builder.ReplaceParent(innerKeyContext, innerParent); if (isGroup) { var inner = (GroupJoinSubQueryContext)innerContext; inner.Join = join.JoinedTable; inner.GetSubQueryContext = () => GetSubQueryContext(builder, methodCall, buildInfo, sql, innerKeyLambda, outerKeySelector, innerKeySelector, outerKeyContext); return(new GroupJoinContext( buildInfo.Parent, selector, context, inner, methodCall.Arguments[1], outerKeyLambda, innerKeyLambda)); } return(new JoinContext(buildInfo.Parent, selector, context, innerContext) #if DEBUG { MethodCall = methodCall } #endif ); }
IBuildContext GetSubQueryContext(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo, SelectQuery sql, LambdaExpression innerKeyLambda, Expression outerKeySelector, Expression innerKeySelector, IBuildContext outerKeyContext) { var subQueryContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); subQueryContext = new SubQueryContext(subQueryContext); var subQueryParent = subQueryContext.Parent; var subQueryKeyContext = new ExpressionContext(buildInfo.Parent, subQueryContext, innerKeyLambda); // Process SubQuery. // var subQuerySql = ((SubQueryContext)subQueryContext).SelectQuery; // Make join and where for the counter. // if (outerKeySelector.NodeType == ExpressionType.New) { var new1 = (NewExpression)outerKeySelector; var new2 = (NewExpression)innerKeySelector; for (var i = 0; i < new1.Arguments.Count; i++) { var arg1 = new1.Arguments[i]; var arg2 = new2.Arguments[i]; BuildSubQueryJoin(builder, outerKeyContext, arg1, arg2, subQueryKeyContext, subQuerySql); } } else if (outerKeySelector.NodeType == ExpressionType.MemberInit) { var mi1 = (MemberInitExpression)outerKeySelector; var mi2 = (MemberInitExpression)innerKeySelector; for (var i = 0; i < mi1.Bindings.Count; i++) { if (mi1.Bindings[i].Member != mi2.Bindings[i].Member) { throw new LinqException($"List of member inits does not match for entity type '{outerKeySelector.Type}'."); } var arg1 = ((MemberAssignment)mi1.Bindings[i]).Expression; var arg2 = ((MemberAssignment)mi2.Bindings[i]).Expression; BuildSubQueryJoin(builder, outerKeyContext, arg1, arg2, subQueryKeyContext, subQuerySql); } } else { BuildSubQueryJoin(builder, outerKeyContext, outerKeySelector, innerKeySelector, subQueryKeyContext, subQuerySql); } builder.ReplaceParent(subQueryKeyContext, subQueryParent); subQuerySql.ParentSelect = sql; subQuerySql.Select.Columns.Clear(); return(subQueryContext); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]) { CreateSubQuery = true }); if (sequence.SelectQuery.Select.IsDistinct || sequence.SelectQuery.Select.TakeValue != null || sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.GroupBy.IsEmpty) { sequence = new SubQueryContext(sequence); } if (sequence.SelectQuery.OrderBy.Items.Count > 0) { if (sequence.SelectQuery.Select.TakeValue == null && sequence.SelectQuery.Select.SkipValue == null) { sequence.SelectQuery.OrderBy.Items.Clear(); } else { sequence = new SubQueryContext(sequence); } } var context = new AggregationContext(buildInfo.Parent, sequence, methodCall); var attr = GetAggregateDefinition(methodCall, builder.MappingSchema); ISqlExpression sqlExpression = null; if (attr != null) { sqlExpression = attr.GetExpression(builder.MappingSchema, sequence.SelectQuery, methodCall, e => { var ex = e.Unwrap(); var l = ex as LambdaExpression; if (l != null) { var p = sequence.Parent; var ctx = new ExpressionContext(buildInfo.Parent, sequence, l); var res = builder.ConvertToSql(ctx, l.Body, true); builder.ReplaceParent(ctx, p); return(res); } return(builder.ConvertToSql(context, ex, true)); }); } if (sqlExpression == null) { var sql = sequence.ConvertToSql(null, 0, ConvertFlags.Field).Select(_ => _.Sql).ToArray(); if (sql.Length == 1 && sql[0] is SelectQuery) { var query = (SelectQuery)sql[0]; if (query.Select.Columns.Count == 1) { var join = query.OuterApply(); context.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); sql[0] = query.Select.Columns[0]; } } if (attr != null) { sqlExpression = attr.GetExpression(methodCall.Method, sql); } else { sqlExpression = new SqlFunction(methodCall.Type, methodCall.Method.Name, true, sql); } } if (sqlExpression == null) { throw new LinqToDBException("Invalid Aggregate function implementation"); } context.Sql = context.SelectQuery; context.FieldIndex = context.SelectQuery.Select.Add(sqlExpression, methodCall.Method.Name); return(context); }
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); }
internal static void ParseSet( ExpressionBuilder builder, BuildInfo buildInfo, LambdaExpression extract, LambdaExpression update, IBuildContext select, SqlTable table, List <SelectQuery.SetExpression> items) { var ext = extract.Body; while (ext.NodeType == ExpressionType.Convert || ext.NodeType == ExpressionType.ConvertChecked) { ext = ((UnaryExpression)ext).Operand; } if (ext.NodeType != ExpressionType.MemberAccess || ext.GetRootObject(builder.MappingSchema) != extract.Parameters[0]) { throw new LinqException("Member expression expected for the 'Set' statement."); } var body = (MemberExpression)ext; var member = body.Member; if (member is MethodInfo) { member = ((MethodInfo)member).GetPropertyInfo(); } var members = body.GetMembers(); var name = members .Skip(1) .Select(ex => { var me = ex as MemberExpression; if (me == null) { return(null); } var m = me.Member; if (m is MethodInfo) { m = ((MethodInfo)m).GetPropertyInfo(); } return(m); }) .Where(m => m != null && !m.IsNullableValueMember()) .Select(m => m.Name) .Aggregate((s1, s2) => s1 + "." + s2); if (table != null && !table.Fields.ContainsKey(name)) { throw new LinqException("Member '{0}.{1}' is not a table column.", member.DeclaringType.Name, name); } var column = table != null ? table.Fields[name] : select.ConvertToSql( body, 1, ConvertFlags.Field)[0].Sql; //Expression.MakeMemberAccess(Expression.Parameter(member.DeclaringType, "p"), member), 1, ConvertFlags.Field)[0].Sql; var sp = select.Parent; var ctx = new ExpressionContext(buildInfo.Parent, select, update); var expr = builder.ConvertToSqlExpression(ctx, update.Body); builder.ReplaceParent(ctx, sp); items.Add(new SelectQuery.SetExpression(column, expr)); }