protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var collectionSelector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); var resultSelector = (LambdaExpression)methodCall.Arguments[2].Unwrap(); if (sequence.SelectQuery.HasUnion || !sequence.SelectQuery.IsSimple) { sequence = new SubQueryContext(sequence); } var context = new SelectManyContext(buildInfo.Parent, collectionSelector, sequence); var expr = collectionSelector.Body.Unwrap(); var collectionInfo = new BuildInfo(context, expr, new SelectQuery()); var collection = builder.BuildSequence(collectionInfo); var leftJoin = collection is DefaultIfEmptyBuilder.DefaultIfEmptyContext || collectionInfo.JoinType == JoinType.Left; var sql = collection.SelectQuery; var sequenceTables = new HashSet <ISqlTableSource>(sequence.SelectQuery.From.Tables[0].GetTables()); var newQuery = null != QueryVisitor.Find(sql, e => e == collectionInfo.SelectQuery); var crossApply = null != QueryVisitor.Find(sql, e => e.ElementType == QueryElementType.TableSource && sequenceTables.Contains((ISqlTableSource)e) || e.ElementType == QueryElementType.SqlField && sequenceTables.Contains(((SqlField)e).Table) || e.ElementType == QueryElementType.Column && sequenceTables.Contains(((SqlColumn)e).Parent)); if (collection is JoinBuilder.GroupJoinSubQueryContext queryContext) { var groupJoin = queryContext.GroupJoin; groupJoin.SelectQuery.From.Tables[0].Joins[0].JoinType = JoinType.Inner; groupJoin.SelectQuery.From.Tables[0].Joins[0].IsWeak = false; } if (!newQuery) { if (collection.SelectQuery.Select.HasModifier) { if (crossApply) { var foundJoin = context.SelectQuery.FindJoin(j => j.Table.Source == collection.SelectQuery); if (foundJoin != null) { foundJoin.JoinType = leftJoin ? JoinType.OuterApply : JoinType.CrossApply; collection.SelectQuery.Where.ConcatSearchCondition(foundJoin.Condition); ((ISqlExpressionWalkable)collection.SelectQuery.Where).Walk(false, e => { if (e is SqlColumn column) { if (column.Parent == collection.SelectQuery) { return(column.UnderlyingColumn); } } return(e); }); foundJoin.Condition.Conditions.Clear(); } } } context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } if (!crossApply) { if (!leftJoin) { context.Collection = new SubQueryContext(collection, sequence.SelectQuery, true); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } else { var join = sql.OuterApply(); sequence.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } } void MoveSearchConditionsToJoin(SqlFromClause.Join join) { var tableSources = new HashSet <ISqlTableSource>(); ((ISqlExpressionWalkable)sql.Where.SearchCondition).Walk(false, e => { if (e is ISqlTableSource ts && !tableSources.Contains(ts)) { tableSources.Add(ts); } return(e); }); bool ContainsTable(ISqlTableSource tbl, IQueryElement qe) { return(null != QueryVisitor.Find(qe, e => e == tbl || e.ElementType == QueryElementType.SqlField && tbl == ((SqlField)e).Table || e.ElementType == QueryElementType.Column && tbl == ((SqlColumn)e).Parent)); } var conditions = sql.Where.SearchCondition.Conditions; if (conditions.Count > 0) { for (var i = conditions.Count - 1; i >= 0; i--) { var condition = conditions[i]; if (!tableSources.Any(ts => ContainsTable(ts, condition))) { join.JoinedTable.Condition.Conditions.Insert(0, condition); conditions.RemoveAt(i); } } } }
static IBuildContext BuildCteContext(ExpressionBuilder builder, BuildInfo buildInfo) { var methodCall = (MethodCallExpression)buildInfo.Expression; Expression bodyExpr; IQueryable?query = null; string? name = null; bool isRecursive = false; switch (methodCall.Arguments.Count) { case 1: bodyExpr = methodCall.Arguments[0].Unwrap(); break; case 2: bodyExpr = methodCall.Arguments[0].Unwrap(); name = methodCall.Arguments[1].EvaluateExpression() as string; break; case 3: query = methodCall.Arguments[0].EvaluateExpression() as IQueryable; bodyExpr = methodCall.Arguments[1].Unwrap(); name = methodCall.Arguments[2].EvaluateExpression() as string; isRecursive = true; break; default: throw new InvalidOperationException(); } bodyExpr = builder.ConvertExpression(bodyExpr); builder.RegisterCte(query, bodyExpr, () => new CteClause(null, bodyExpr.Type.GetGenericArguments()[0], isRecursive, name)); var cte = builder.BuildCte(bodyExpr, cteClause => { var info = new BuildInfo(buildInfo, bodyExpr, new SelectQuery()); var sequence = builder.BuildSequence(info); if (cteClause == null) { cteClause = new CteClause(sequence.SelectQuery, bodyExpr.Type.GetGenericArguments()[0], isRecursive, name); } else { cteClause.Body = sequence.SelectQuery; cteClause.Name = name; } return(Tuple.Create(cteClause, (IBuildContext?)sequence)); } ); var cteBuildInfo = new BuildInfo(buildInfo, bodyExpr, buildInfo.SelectQuery); var cteContext = new CteTableContext(builder, cteBuildInfo, cte.Item1, bodyExpr); // populate all fields if (isRecursive) { cteContext.ConvertToSql(null, 0, ConvertFlags.All); } return(cteContext); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var kind = MergeKind.Merge; if (methodCall.IsSameGenericMethod(MergeWithOutputInto)) { kind = MergeKind.MergeWithOutputInto; } else if (methodCall.IsSameGenericMethod(MergeWithOutput)) { kind = MergeKind.MergeWithOutput; } if (kind != MergeKind.Merge) { var objectType = methodCall.Method.GetGenericArguments()[0]; var actionField = SqlField.FakeField(new DbDataType(typeof(string)), "$action", false); var insertedTable = SqlTable.Inserted(objectType); var deletedTable = SqlTable.Deleted(objectType); mergeContext.Merge.Output = new SqlOutputClause() { InsertedTable = insertedTable, DeletedTable = deletedTable, }; var selectQuery = new SelectQuery(); var actionFieldContext = new SingleExpressionContext(null, builder, actionField, selectQuery); var deletedTableContext = new TableBuilder.TableContext(builder, selectQuery, deletedTable); var insertedTableConext = new TableBuilder.TableContext(builder, selectQuery, insertedTable); if (kind == MergeKind.MergeWithOutput) { var outputExpression = (LambdaExpression)methodCall.Arguments[1].Unwrap(); var outputContext = new MergeOutputContext( buildInfo.Parent, outputExpression, mergeContext, actionFieldContext, deletedTableContext, insertedTableConext ); return(outputContext); } else { var outputExpression = (LambdaExpression)methodCall.Arguments[2].Unwrap(); var outputTable = methodCall.Arguments[1]; var destination = builder.BuildSequence(new BuildInfo(buildInfo, outputTable, new SelectQuery())); UpdateBuilder.BuildSetterWithContext( builder, buildInfo, outputExpression, destination, mergeContext.Merge.Output.OutputItems, actionFieldContext, deletedTableContext, insertedTableConext ); mergeContext.Merge.Output.OutputTable = ((TableBuilder.TableContext)destination).SqlTable; } } return(mergeContext); }
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])); var wrapped = false; if (sequence.SelectQuery.Select.HasModifier) { sequence = new SubQueryContext(sequence); wrapped = true; } var isContinuousOrder = !sequence.SelectQuery.OrderBy.IsEmpty && methodCall.Method.Name.StartsWith("Then"); 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); // Do not create subquery for ThenByExtensions if (wrapped || isContinuousOrder) { 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 != sqlInfo.Sql.Find(QueryElementType.SqlQuery); if (isComplex) { break; } } if (!isComplex) { break; } sequence = new SubQueryContext(sequence); wrapped = true; } if (!isContinuousOrder && !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; } sequence.SelectQuery.OrderBy.Expr(expr.Sql, 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])); return(new ContainsContext(buildInfo.Parent, methodCall, 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); foreach (var sql in groupSql.Where(s => s.Sql.ElementType.NotIn(QueryElementType.SqlValue, QueryElementType.SqlParameter))) { 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]) { 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(); if (ex is LambdaExpression l) { 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)); }); } var methodName = methodCall.Method.Name.Replace("Async", ""); if (sqlExpression == null) { var sql = sequence.ConvertToSql(null, 0, ConvertFlags.Field).Select(_ => _.Sql).ToArray(); if (sql.Length == 1 && sql[0] is SelectQuery query) { 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, methodName, true, sql); } } if (sqlExpression == null) { throw new LinqToDBException("Invalid Aggregate function implementation"); } context.Sql = context.SelectQuery; context.FieldIndex = context.SelectQuery.Select.Add(sqlExpression, methodName); return(context); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var collectionSelector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); var resultSelector = (LambdaExpression)methodCall.Arguments[2].Unwrap(); if (!sequence.SelectQuery.GroupBy.IsEmpty) { sequence = new SubQueryContext(sequence); } var context = new SelectManyContext(buildInfo.Parent, collectionSelector, sequence); var expr = collectionSelector.Body.Unwrap(); var collectionInfo = new BuildInfo(context, expr, new SelectQuery()); var collection = builder.BuildSequence(collectionInfo); var leftJoin = collection is DefaultIfEmptyBuilder.DefaultIfEmptyContext; var sql = collection.SelectQuery; var sequenceTables = new HashSet <ISqlTableSource>(sequence.SelectQuery.From.Tables[0].GetTables()); var newQuery = null != new QueryVisitor().Find(sql, e => e == collectionInfo.SelectQuery); var crossApply = null != new QueryVisitor().Find(sql, e => e.ElementType == QueryElementType.TableSource && sequenceTables.Contains((ISqlTableSource)e) || e.ElementType == QueryElementType.SqlField && sequenceTables.Contains(((SqlField)e).Table) || e.ElementType == QueryElementType.Column && sequenceTables.Contains(((SelectQuery.Column)e).Parent)); if (collection is JoinBuilder.GroupJoinSubQueryContext) { var groupJoin = ((JoinBuilder.GroupJoinSubQueryContext)collection).GroupJoin; groupJoin.SelectQuery.From.Tables[0].Joins[0].JoinType = SelectQuery.JoinType.Inner; groupJoin.SelectQuery.From.Tables[0].Joins[0].IsWeak = false; } if (!newQuery) { context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } if (!crossApply) { if (!leftJoin) { context.Collection = new SubQueryContext(collection, sequence.SelectQuery, true); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } else { var join = SelectQuery.OuterApply(sql); sequence.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } } if (collection is TableBuilder.TableContext) { // if (collectionInfo.IsAssociationBuilt) // { // context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false); // return new SelectContext(buildInfo.Parent, resultSelector, sequence, context); // } var table = (TableBuilder.TableContext)collection; var join = table.SqlTable.TableArguments != null && table.SqlTable.TableArguments.Length > 0 ? (leftJoin ? SelectQuery.OuterApply(sql) : SelectQuery.CrossApply(sql)) : (leftJoin ? SelectQuery.LeftJoin(sql) : SelectQuery.InnerJoin(sql)); join.JoinedTable.Condition.Conditions.AddRange(sql.Where.SearchCondition.Conditions); join.JoinedTable.CanConvertApply = false; sql.Where.SearchCondition.Conditions.Clear(); var collectionParent = collection.Parent as TableBuilder.TableContext; // Association. // if (collectionParent != null && collectionInfo.IsAssociationBuilt) { var ts = (SelectQuery.TableSource) new QueryVisitor().Find(sequence.SelectQuery.From, e => { if (e.ElementType == QueryElementType.TableSource) { var t = (SelectQuery.TableSource)e; return(t.Source == collectionParent.SqlTable); } return(false); }); ts.Joins.Add(join.JoinedTable); } else { //if (collectionInfo.IsAssociationBuilt) //{ // collectionInfo.AssosiationContext.ParentAssociationJoin.IsWeak = false; //} //else { sequence.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); } } context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } else { var join = leftJoin ? SelectQuery.OuterApply(sql) : SelectQuery.CrossApply(sql); sequence.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false); return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context)); } }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var isSubQuery = sequence.SelectQuery.Select.IsDistinct; if (isSubQuery) { sequence = new SubQueryContext(sequence); } if (!(sequence.Statement is SqlInsertStatement insertStatement)) { insertStatement = new SqlInsertStatement(sequence.SelectQuery); sequence.Statement = insertStatement; } switch (methodCall.Arguments.Count) { case 1: // static int Insert<T> (this IValueInsertable<T> source) // static int Insert<TSource,TTarget>(this ISelectInsertable<TSource,TTarget> source) { sequence.SelectQuery.Select.Columns.Clear(); foreach (var item in insertStatement.Insert.Items) { sequence.SelectQuery.Select.ExprNew(item.Expression); } break; } case 2: // static int Insert<T>(this Table<T> target, Expression<Func<T>> setter) { UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[1].Unwrap(), sequence, insertStatement.Insert.Items, sequence); insertStatement.Insert.Into = ((TableBuilder.TableContext)sequence).SqlTable; sequence.SelectQuery.From.Tables.Clear(); break; } case 3: // static int Insert<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter) { var into = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), into, insertStatement.Insert.Items, sequence); sequence.SelectQuery.Select.Columns.Clear(); foreach (var item in insertStatement.Insert.Items) { sequence.SelectQuery.Select.Columns.Add(new SqlColumn(sequence.SelectQuery, item.Expression)); } insertStatement.Insert.Into = ((TableBuilder.TableContext)into).SqlTable; break; } } var insert = insertStatement.Insert; var q = insert.Into.Fields.Values .Except(insert.Items.Select(e => e.Column)) .OfType <SqlField>() .Where(f => f.IsIdentity); foreach (var field in q) { var expr = builder.DataContext.CreateSqlProvider().GetIdentityExpression(insert.Into); if (expr != null) { insert.Items.Insert(0, new SqlSetExpression(field, expr)); if (methodCall.Arguments.Count == 3) { sequence.SelectQuery.Select.Columns.Insert(0, new SqlColumn(sequence.SelectQuery, insert.Items[0].Expression)); } } } insertStatement.Insert.WithIdentity = methodCall.Method.Name == "InsertWithIdentity"; sequence.Statement = insertStatement; return(new InsertContext(buildInfo.Parent, sequence, insertStatement.Insert.WithIdentity)); }
public AssociatedTableContext( [JetBrains.Annotations.NotNull] ExpressionBuilder builder, [JetBrains.Annotations.NotNull] TableContext parent, [JetBrains.Annotations.NotNull] AssociationDescriptor association ) : base(builder, parent.SelectQuery) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (parent == null) { throw new ArgumentNullException(nameof(parent)); } if (association == null) { throw new ArgumentNullException(nameof(association)); } var type = association.MemberInfo.GetMemberType(); var left = association.CanBeNull; if (typeof(IEnumerable).IsSameOrParentOf(type)) { var eTypes = type.GetGenericArguments(typeof(IEnumerable <>)); type = eTypes != null && eTypes.Length > 0 ? eTypes[0] : type.GetListItemType(); IsList = true; } OriginalType = type; ObjectType = GetObjectType(); EntityDescriptor = Builder.MappingSchema.GetEntityDescriptor(ObjectType); InheritanceMapping = EntityDescriptor.InheritanceMapping; SqlTable = new SqlTable(builder.MappingSchema, ObjectType); Association = association; ParentAssociation = parent; SqlJoinedTable join; var queryMethod = Association.GetQueryMethod(parent.ObjectType, ObjectType); if (queryMethod != null) { var selectManyMethod = GetAssociationQueryExpression(Expression.Constant(builder.DataContext), queryMethod.Parameters[0], parent.ObjectType, parent.Expression, queryMethod); var ownerTableSource = SelectQuery.From.Tables[0]; var buildInfo = new BuildInfo(this, selectManyMethod, new SelectQuery()) { IsAssociationBuilt = true }; _innerContext = builder.BuildSequence(buildInfo); if (Association.CanBeNull) { _innerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, _innerContext, null); } var associationQuery = _innerContext.SelectQuery; if (associationQuery.Select.From.Tables.Count < 1) { throw new LinqToDBException("Invalid association query. It is not possible to inline query."); } var foundIndex = associationQuery.Select.From.Tables.FindIndex(t => t.Source is SqlTable sqlTable && QueryHelper.IsEqualTables(sqlTable, parent.SqlTable)); // try to search table by object type // TODO: review maybe there are another ways to do that if (foundIndex < 0) { foundIndex = associationQuery.Select.From.Tables.FindIndex(t => t.Source is SqlTable sqlTable && sqlTable.ObjectType == parent.SqlTable.ObjectType); } if (foundIndex < 0) { throw new LinqToDBException("Invalid association query. It is not possible to inline query. Can not find owner table."); } var sourceToReplace = associationQuery.Select.From.Tables[foundIndex]; if (left) { foreach (var joinedTable in sourceToReplace.Joins) { if (joinedTable.JoinType == JoinType.Inner) { joinedTable.JoinType = JoinType.Left; } else if (joinedTable.JoinType == JoinType.CrossApply) { joinedTable.JoinType = JoinType.OuterApply; } joinedTable.IsWeak = true; } } ownerTableSource.Joins.AddRange(sourceToReplace.Joins); // prepare fields mapping to replace fields that will be generated by association query _replaceMap = ((SqlTable)sourceToReplace.Source).Fields.Values.ToDictionary(f => (ISqlExpression)f, f => parent.SqlTable.Fields[f.Name]); ownerTableSource.Walk(new WalkOptions(), e => { if (_replaceMap.TryGetValue(e, out var newField)) { return(newField); } return(e); }); ParentAssociationJoin = sourceToReplace.Joins.FirstOrDefault(); join = ParentAssociationJoin; // add rest of tables SelectQuery.From.Tables.AddRange(associationQuery.Select.From.Tables.Where(t => t != sourceToReplace)); //TODO: Change AssociatedTableContext base class //SqlTable = null; } else { var psrc = parent.SelectQuery.From[parent.SqlTable]; join = left ? SqlTable.WeakLeftJoin().JoinedTable : SqlTable.WeakInnerJoin().JoinedTable; ParentAssociationJoin = join; psrc.Joins.Add(join); for (var i = 0; i < association.ThisKey.Length; i++) { if (!parent.SqlTable.Fields.TryGetValue(association.ThisKey[i], out var field1)) { throw new LinqException("Association key '{0}' not found for type '{1}.", association.ThisKey[i], parent.ObjectType); } if (!SqlTable.Fields.TryGetValue(association.OtherKey[i], out var field2)) { throw new LinqException("Association key '{0}' not found for type '{1}.", association.OtherKey[i], ObjectType); } // join.Field(field1).Equal.Field(field2); ISqlPredicate predicate = new SqlPredicate.ExprExpr( field1, SqlPredicate.Operator.Equal, field2); predicate = builder.Convert(parent, predicate); join.Condition.Conditions.Add(new SqlCondition(false, predicate)); } if (ObjectType != OriginalType) { var predicate = Builder.MakeIsPredicate(this, OriginalType); if (predicate.GetType() != typeof(SqlPredicate.Expr)) { join.Condition.Conditions.Add(new SqlCondition(false, predicate)); } } RegularConditionCount = join.Condition.Conditions.Count; ExpressionPredicate = Association.GetPredicate(parent.ObjectType, ObjectType); if (ExpressionPredicate != null) { ExpressionPredicate = (LambdaExpression)Builder.ConvertExpressionTree(ExpressionPredicate); var expr = Builder.ConvertExpression(ExpressionPredicate.Body.Unwrap()); Builder.BuildSearchCondition( new ExpressionContext(parent.Parent, new IBuildContext[] { parent, this }, ExpressionPredicate), expr, join.Condition.Conditions, false); } } if (!association.AliasName.IsNullOrEmpty() && join != null) { join.Table.Alias = association.AliasName; } else { if (!Common.Configuration.Sql.AssociationAlias.IsNullOrEmpty() && join != null) { join.Table.Alias = string.Format(Common.Configuration.Sql.AssociationAlias, association.MemberInfo.Name); } } Init(false); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var statement = mergeContext.Merge; var operation = new SqlMergeOperationClause(MergeOperationType.Insert); statement.Operations.Add(operation); var predicate = methodCall.Arguments[1]; var setter = methodCall.Arguments[2]; if (!setter.IsNullValue()) { var setterExpression = (LambdaExpression)setter.Unwrap(); mergeContext.AddSourceParameter(setterExpression.Parameters[0]); UpdateBuilder.BuildSetterWithContext( builder, buildInfo, setterExpression, mergeContext.TargetContext, operation.Items, mergeContext.SourceContext); } else { // build setters like QueryRunner.Insert var sqlTable = (SqlTable)statement.Target.Source; var param = Expression.Parameter(sqlTable.ObjectType, "s"); foreach (var field in sqlTable.Fields) { if (field.IsInsertable) { var expression = LinqToDB.Expressions.Extensions.GetMemberGetter(field.ColumnDescriptor.MemberInfo, param); var tgtExpr = mergeContext.TargetContext.ConvertToSql(builder.ConvertExpression(expression), 1, ConvertFlags.Field)[0].Sql; var srcExpr = mergeContext.SourceContext.ConvertToSql(builder.ConvertExpression(expression), 1, ConvertFlags.Field)[0].Sql; operation.Items.Add(new SqlSetExpression(tgtExpr, srcExpr)); } else if (field.IsIdentity) { var expr = builder.DataContext.CreateSqlProvider().GetIdentityExpression(sqlTable); if (expr != null) { operation.Items.Add(new SqlSetExpression(field, expr)); } } } } if (!predicate.IsNullValue()) { var condition = (LambdaExpression)predicate.Unwrap(); var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap()); operation.Where = new SqlSearchCondition(); builder.BuildSearchCondition( new ExpressionContext(null, new[] { mergeContext.SourceContext }, condition), conditionExpr, operation.Where.Conditions); } return(mergeContext); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); switch (methodCall.Arguments.Count) { case 1: // int Update<T>(this IUpdateable<T> source) CheckAssociation(sequence); break; case 2: // int Update<T>(this IQueryable<T> source, Expression<Func<T,T>> setter) { CheckAssociation(sequence); BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[1].Unwrap(), sequence, sequence.SelectQuery.Update.Items, sequence); break; } case 3: { var expr = methodCall.Arguments[1].Unwrap(); if (expr is LambdaExpression) { CheckAssociation(sequence); // int Update<T>(this IQueryable<T> source, Expression<Func<T,bool>> predicate, Expression<Func<T,T>> setter) // sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false); BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), sequence, sequence.SelectQuery.Update.Items, sequence); } else { // static int Update<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter) // var into = builder.BuildSequence(new BuildInfo(buildInfo, expr, new SelectQuery())); sequence.ConvertToIndex(null, 0, ConvertFlags.All); new SelectQueryOptimizer(builder.DataContextInfo.SqlProviderFlags, sequence.SelectQuery) .ResolveWeakJoins(new List <ISqlTableSource>()); sequence.SelectQuery.Select.Columns.Clear(); BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), into, sequence.SelectQuery.Update.Items, sequence); var sql = sequence.SelectQuery; sql.Select.Columns.Clear(); foreach (var item in sql.Update.Items) { sql.Select.Columns.Add(new SelectQuery.Column(sql, item.Expression)); } sql.Update.Table = ((TableBuilder.TableContext)into).SqlTable; } break; } } sequence.SelectQuery.QueryType = QueryType.Update; return(new UpdateContext(buildInfo.Parent, sequence)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var deleteType = methodCall.Method.Name switch { nameof(LinqExtensions.DeleteWithOutput) => DeleteContext.DeleteType.DeleteOutput, nameof(LinqExtensions.DeleteWithOutputInto) => DeleteContext.DeleteType.DeleteOutputInto, _ => DeleteContext.DeleteType.Delete, }; var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); if (methodCall.Arguments.Count == 2 && deleteType == DeleteContext.DeleteType.Delete) { sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false); } var deleteStatement = new SqlDeleteStatement(sequence.SelectQuery); sequence.Statement = deleteStatement; // Check association. // if (sequence is SelectContext ctx && ctx.IsScalar) { var res = ctx.IsExpression(null, 0, RequestFor.Association); if (res.Result) { var isTableResult = res.Context !.IsExpression(null, 0, RequestFor.Table); if (!isTableResult.Result) { throw new LinqException("Can not retrieve Table context from association."); } var atc = (TableBuilder.TableContext)isTableResult.Context !; deleteStatement.Table = atc.SqlTable; } else { res = ctx.IsExpression(null, 0, RequestFor.Table); if (res.Result && res.Context is TableBuilder.TableContext context) { var tc = context; if (deleteStatement.SelectQuery.From.Tables.Count == 0 || deleteStatement.SelectQuery.From.Tables[0].Source != tc.SelectQuery) { deleteStatement.Table = tc.SqlTable; } } } } var indexedParameters = methodCall.Method.GetParameters().Select((p, i) => Tuple.Create(p, i)).ToDictionary(t => t.Item1.Name, t => t.Item2); Expression GetArgumentByName(string name) { return(methodCall.Arguments[indexedParameters[name]]); } LambdaExpression GetOutputExpression(Type outputType) { if (!indexedParameters.TryGetValue("outputExpression", out var index)) { var param = Expression.Parameter(outputType); return(Expression.Lambda(param, param)); } return((LambdaExpression)methodCall.Arguments[index].Unwrap()); } IBuildContext? outputContext = null; LambdaExpression?outputExpression = null; if (deleteType != DeleteContext.DeleteType.Delete) { outputExpression = GetOutputExpression(methodCall.Method.GetGenericArguments().Last()); deleteStatement.Output = new SqlOutputClause(); var deletedTable = SqlTable.Deleted(methodCall.Method.GetGenericArguments()[0]); outputContext = new TableBuilder.TableContext(builder, new SelectQuery(), deletedTable); deleteStatement.Output.DeletedTable = deletedTable; if (deleteType == DeleteContext.DeleteType.DeleteOutputInto) { var outputTable = GetArgumentByName("outputTable"); var destination = builder.BuildSequence(new BuildInfo(buildInfo, outputTable, new SelectQuery())); UpdateBuilder.BuildSetter( builder, buildInfo, outputExpression, destination, deleteStatement.Output.OutputItems, outputContext); deleteStatement.Output.OutputTable = ((TableBuilder.TableContext)destination).SqlTable; } } if (deleteType == DeleteContext.DeleteType.DeleteOutput) { return(new DeleteWithOutputContext(buildInfo.Parent, sequence, outputContext !, outputExpression !)); } return(new DeleteContext(buildInfo.Parent, sequence)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]))); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]) { CreateSubQuery = true }); var returnType = methodCall.Method.ReturnType; if (methodCall.IsAsyncExtension()) { returnType = returnType.GetGenericArguments()[0]; } if (sequence.SelectQuery != buildInfo.SelectQuery) { if (sequence is JoinBuilder.GroupJoinSubQueryContext) { var ctx = new CountContext(buildInfo.Parent, sequence, returnType) { SelectQuery = sequence.SelectQuery //((JoinBuilder.GroupJoinSubQueryContext)sequence).GetCounter(methodCall) }; ctx.Sql = ctx.SelectQuery; ctx.FieldIndex = ctx.SelectQuery.Select.Add(SqlFunction.CreateCount(returnType, ctx.SelectQuery), "cnt"); return(ctx); } if (sequence is GroupByBuilder.GroupByContext) { // var ctx = new CountContext(buildInfo.Parent, sequence, returnType); // // ctx.Sql = ctx.SelectQuery; // ctx.FieldIndex = ctx.SelectQuery.Select.Add(SqlFunction.CreateCount(returnType, ctx.SelectQuery), "cnt"); // // return ctx; // return new CountContext(buildInfo.Parent, sequence, returnType) // { // Sql = SqlFunction.CreateCount(returnType, sequence.SelectQuery), // FieldIndex = -1 // }; } } if (sequence.SelectQuery.Select.IsDistinct || sequence.SelectQuery.Select.TakeValue != null || sequence.SelectQuery.Select.SkipValue != null) { sequence.ConvertToIndex(null, 0, ConvertFlags.Key); sequence = new SubQueryContext(sequence); } else if (!sequence.SelectQuery.GroupBy.IsEmpty) { if (!builder.DataContext.SqlProviderFlags.IsSybaseBuggyGroupBy) { sequence.SelectQuery.Select.Add(new SqlValue(0)); } else { foreach (var item in sequence.SelectQuery.GroupBy.Items) { sequence.SelectQuery.Select.Add(item); } } 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 CountContext(buildInfo.Parent, sequence, returnType); context.Sql = context.SelectQuery; context.FieldIndex = context.SelectQuery.Select.Add(SqlFunction.CreateCount(returnType, context.SelectQuery), "cnt"); return(context); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence1 = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var sequence2 = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); SetOperation setOperation; switch (methodCall.Method.Name) { case "Concat": case "UnionAll": setOperation = SetOperation.UnionAll; break; case "Union": setOperation = SetOperation.Union; break; case "Except": setOperation = SetOperation.Except; break; case "ExceptAll": setOperation = SetOperation.ExceptAll; break; case "Intersect": setOperation = SetOperation.Intersect; break; case "IntersectAll": setOperation = SetOperation.IntersectAll; break; default: throw new ArgumentException($"Invalid method name {methodCall.Method.Name}."); } var needsEmulation = !builder.DataContext.SqlProviderFlags.IsAllSetOperationsSupported && (setOperation == SetOperation.ExceptAll || setOperation == SetOperation.IntersectAll) || !builder.DataContext.SqlProviderFlags.IsDistinctSetOperationsSupported && (setOperation == SetOperation.Except || setOperation == SetOperation.Intersect); if (needsEmulation) { // emulation var sequence = new SubQueryContext(sequence1); var query = sequence2; var except = query.SelectQuery; var sql = sequence.SelectQuery; if (setOperation == SetOperation.Except || setOperation == SetOperation.Intersect) { sql.Select.IsDistinct = true; } except.ParentSelect = sql; if (setOperation == SetOperation.Except || setOperation == SetOperation.ExceptAll) { sql.Where.Not.Exists(except); } else { sql.Where.Exists(except); } var keys1 = sequence.ConvertToSql(null, 0, ConvertFlags.All); var keys2 = query.ConvertToSql(null, 0, ConvertFlags.All); if (keys1.Length != keys2.Length) { throw new InvalidOperationException(); } for (var i = 0; i < keys1.Length; i++) { except.Where .Expr(keys1[i].Sql) .Equal .Expr(keys2[i].Sql); } return(sequence); } var set1 = new SubQueryContext(sequence1); var set2 = new SubQueryContext(sequence2); var setOperator = new SqlSetOperator(set2.SelectQuery, setOperation); set1.SelectQuery.SetOperators.Add(setOperator); return(new SetOperationContext(set1, set2, methodCall)); }
public AssociatedTableContext( ExpressionBuilder builder, TableContext parent, AssociationDescriptor association, bool forceLeft, bool asSubquery ) : base(builder, asSubquery ? new SelectQuery() { ParentSelect = parent.SelectQuery } : parent.SelectQuery) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (parent == null) { throw new ArgumentNullException(nameof(parent)); } if (association == null) { throw new ArgumentNullException(nameof(association)); } var type = association.MemberInfo.GetMemberType(); var left = forceLeft || association.CanBeNull; if (typeof(IEnumerable).IsSameOrParentOf(type)) { var eTypes = type.GetGenericArguments(typeof(IEnumerable <>)); type = eTypes != null && eTypes.Length > 0 ? eTypes[0] : type.GetListItemType(); IsList = true; } OriginalType = type; ObjectType = GetObjectType(); EntityDescriptor = Builder.MappingSchema.GetEntityDescriptor(ObjectType); InheritanceMapping = EntityDescriptor.InheritanceMapping; SqlTable = new SqlTable(builder.MappingSchema, ObjectType); Association = association; ParentAssociation = parent; IsSubQuery = asSubquery; if (asSubquery) { BuildSubQuery(builder, parent, association, left); Init(false); return; } SqlJoinedTable join; var queryMethod = Association.GetQueryMethod(parent.ObjectType, ObjectType); if (queryMethod != null) { var selectManyMethod = GetAssociationQueryExpression(Expression.Constant(builder.DataContext), queryMethod.Parameters[0], parent.ObjectType, parent.Expression !, queryMethod); var ownerTableSource = SelectQuery.From.Tables[0]; var buildInfo = new BuildInfo(this, selectManyMethod, new SelectQuery()) { IsAssociationBuilt = true }; _innerContext = builder.BuildSequence(buildInfo); if (Association.CanBeNull) { _innerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, _innerContext, null); } var associationQuery = _innerContext.SelectQuery; if (associationQuery.Select.From.Tables.Count < 1) { throw new LinqToDBException("Invalid association query. It is not possible to inline query."); } var foundIndex = associationQuery.Select.From.Tables.FindIndex(t => t.Source is SqlTable sqlTable && QueryHelper.IsEqualTables(sqlTable, parent.SqlTable)); // try to search table by object type // TODO: review maybe there are another way to do that if (foundIndex < 0) { foundIndex = associationQuery.Select.From.Tables.FindIndex(t => t.Source is SqlTable sqlTable && sqlTable.ObjectType == parent.SqlTable.ObjectType); } if (foundIndex < 0) { throw new LinqToDBException("Invalid association query. It is not possible to inline query. Can not find owner table."); } var sourceToReplace = associationQuery.Select.From.Tables[foundIndex]; if (left) { foreach (var joinedTable in sourceToReplace.Joins) { if (joinedTable.JoinType == JoinType.Inner) { joinedTable.JoinType = JoinType.Left; } else if (joinedTable.JoinType == JoinType.CrossApply) { joinedTable.JoinType = JoinType.OuterApply; } joinedTable.IsWeak = true; } } ownerTableSource.Joins.AddRange(sourceToReplace.Joins); // prepare fields mapping to replace fields that will be generated by association query _replaceMap = ((SqlTable)sourceToReplace.Source).Fields.Values.ToDictionary(f => (ISqlExpression)f, f => parent.SqlTable.Fields[f.Name]); ownerTableSource.Walk(new WalkOptions(), e => { if (_replaceMap.TryGetValue(e, out var newField)) { return(newField); } return(e); }); ParentAssociationJoin = sourceToReplace.Joins.FirstOrDefault(); join = ParentAssociationJoin; // add rest of tables SelectQuery.From.Tables.AddRange(associationQuery.Select.From.Tables.Where(t => t != sourceToReplace)); //TODO: Change AssociatedTableContext base class //SqlTable = null; } else { var psrc = parent.SelectQuery.From[parent.SqlTable] !; join = left ? SqlTable.WeakLeftJoin().JoinedTable : SqlTable.WeakInnerJoin().JoinedTable; ParentAssociationJoin = join; psrc.Joins.Add(join); BuildAssociationCondition(builder, parent, association, join.Condition); } SetTableAlias(association, join?.Table); Init(false); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var isSubQuery = sequence.SelectQuery.Select.IsDistinct; if (isSubQuery) { sequence = new SubQueryContext(sequence); } if (!(sequence.Statement is SqlInsertStatement insertStatement)) { insertStatement = new SqlInsertStatement(sequence.SelectQuery); sequence.Statement = insertStatement; } var insertType = InsertContext.InsertType.Insert; switch (methodCall.Method.Name) { case "Insert": insertType = InsertContext.InsertType.Insert; break; case "InsertWithIdentity": insertType = InsertContext.InsertType.InsertWithIdentity; break; case "InsertWithOutput": insertType = InsertContext.InsertType.InsertOutput; break; case "InsertWithOutputInto": insertType = InsertContext.InsertType.InsertOutputInto; break; } var indexedParameters = methodCall.Method.GetParameters().Select((p, i) => Tuple.Create(p, i)).ToDictionary(t => t.Item1.Name, t => t.Item2); Expression GetArgumentByName(string name) { return(methodCall.Arguments[indexedParameters[name]]); } LambdaExpression GetOutputExpression(Type outputType) { if (!indexedParameters.TryGetValue("outputExpression", out var index)) { var param = Expression.Parameter(outputType); return(Expression.Lambda(param, param)); } return((LambdaExpression)methodCall.Arguments[index].Unwrap()); } IBuildContext? outputContext = null; LambdaExpression?outputExpression = null; if (methodCall.Arguments.Count > 0) { var argument = methodCall.Arguments[0]; if (typeof(IValueInsertable <>).IsSameOrParentOf(argument.Type) || typeof(ISelectInsertable <,>).IsSameOrParentOf(argument.Type)) { // static int Insert<T> (this IValueInsertable<T> source) // static int Insert<TSource,TTarget>(this ISelectInsertable<TSource,TTarget> source) sequence.SelectQuery.Select.Columns.Clear(); foreach (var item in insertStatement.Insert.Items) { sequence.SelectQuery.Select.ExprNew(item.Expression !); } } else if (methodCall.Arguments.Count > 1 && typeof(IQueryable <>).IsSameOrParentOf(argument.Type) && typeof(ITable <>).IsSameOrParentOf(methodCall.Arguments[1].Type)) { // static int Insert<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter) var into = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); var setter = (LambdaExpression)GetArgumentByName("setter").Unwrap(); UpdateBuilder.BuildSetter( builder, buildInfo, setter, into, insertStatement.Insert.Items, sequence); sequence.SelectQuery.Select.Columns.Clear(); foreach (var item in insertStatement.Insert.Items) { sequence.SelectQuery.Select.Columns.Add(new SqlColumn(sequence.SelectQuery, item.Expression !)); } insertStatement.Insert.Into = ((TableBuilder.TableContext)into).SqlTable; } else if (typeof(ITable <>).IsSameOrParentOf(argument.Type)) { // static int Insert<T>(this Table<T> target, Expression<Func<T>> setter) // static TTarget InsertWithOutput<TTarget>(this ITable<TTarget> target, Expression<Func<TTarget>> setter) // static TTarget InsertWithOutput<TTarget>(this ITable<TTarget> target, Expression<Func<TTarget>> setter, Expression<Func<TTarget,TOutput>> outputExpression) var argIndex = 1; var arg = methodCall.Arguments[argIndex].Unwrap(); LambdaExpression?setter = null; switch (arg) { case LambdaExpression lambda: { setter = lambda; UpdateBuilder.BuildSetter( builder, buildInfo, setter, sequence, insertStatement.Insert.Items, sequence); break; } default: { var objType = arg.Type; var ed = builder.MappingSchema.GetEntityDescriptor(objType); var into = sequence; var ctx = new TableBuilder.TableContext(builder, buildInfo, objType); var table = new SqlTable(objType); foreach (var c in ed.Columns.Where(c => !c.SkipOnInsert)) { var field = table[c.ColumnName]; if (field == null) { continue; } var pe = Expression.MakeMemberAccess(arg, c.MemberInfo); var column = into.ConvertToSql(pe, 1, ConvertFlags.Field); var parameter = builder.BuildParameterFromArgumentProperty(methodCall, argIndex, field.ColumnDescriptor); insertStatement.Insert.Items.Add(new SqlSetExpression(column[0].Sql, parameter.SqlParameter)); } var insertedTable = SqlTable.Inserted(methodCall.Method.GetGenericArguments()[0]); break; } } insertStatement.Insert.Into = ((TableBuilder.TableContext)sequence).SqlTable; sequence.SelectQuery.From.Tables.Clear(); } if (insertType == InsertContext.InsertType.InsertOutput || insertType == InsertContext.InsertType.InsertOutputInto) { outputExpression = GetOutputExpression(methodCall.Method.GetGenericArguments().Last()); insertStatement.Output = new SqlOutputClause(); var insertedTable = SqlTable.Inserted(outputExpression.Parameters[0].Type); outputContext = new TableBuilder.TableContext(builder, new SelectQuery(), insertedTable); insertStatement.Output.InsertedTable = insertedTable; if (insertType == InsertContext.InsertType.InsertOutputInto) { var outputTable = GetArgumentByName("outputTable"); var destination = builder.BuildSequence(new BuildInfo(buildInfo, outputTable, new SelectQuery())); UpdateBuilder.BuildSetter( builder, buildInfo, outputExpression, destination, insertStatement.Output.OutputItems, outputContext); insertStatement.Output.OutputTable = ((TableBuilder.TableContext)destination).SqlTable; } } } var insert = insertStatement.Insert; var q = insert.Into !.IdentityFields .Except(insert.Items.Select(e => e.Column).OfType <SqlField>()); foreach (var field in q) { var expr = builder.DataContext.CreateSqlProvider().GetIdentityExpression(insert.Into); if (expr != null) { insert.Items.Insert(0, new SqlSetExpression(field, expr)); if (methodCall.Arguments.Count == 3) { sequence.SelectQuery.Select.Columns.Insert(0, new SqlColumn(sequence.SelectQuery, insert.Items[0].Expression !)); } } } insertStatement.Insert.WithIdentity = insertType == InsertContext.InsertType.InsertWithIdentity; sequence.Statement = insertStatement; if (insertType == InsertContext.InsertType.InsertOutput) { return(new InsertWithOutputContext(buildInfo.Parent, sequence, outputContext !, outputExpression !)); } return(new InsertContext(buildInfo.Parent, sequence, insertType, outputExpression)); }
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); builder.ReplaceParent(order, sparent); if (wrapped) { break; } // handle situation when order by uses complex field var isComplex = false; foreach (var sqlInfo in sql) { // 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) { var e = builder.ConvertSearchCondition(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])); switch (methodCall.Arguments.Count) { case 1: // int Update<T>(this IUpdateable<T> source) CheckAssociation(sequence); break; case 2: // int Update<T>(this IQueryable<T> source, Expression<Func<T,T>> setter) { CheckAssociation(sequence); if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty) { sequence = new SubQueryContext(sequence); } BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[1].Unwrap(), sequence, sequence.SelectQuery.Update.Items, sequence); break; } case 3: { var expr = methodCall.Arguments[1].Unwrap(); if (expr is LambdaExpression && ((LambdaExpression)expr).ReturnType == typeof(bool)) { CheckAssociation(sequence); // int Update<T>(this IQueryable<T> source, Expression<Func<T,bool>> predicate, Expression<Func<T,T>> setter) // sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false); if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty) { sequence = new SubQueryContext(sequence); } BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), sequence, sequence.SelectQuery.Update.Items, sequence); } else { IBuildContext into; if (expr is LambdaExpression) { // static int Update<TSource,TTarget>(this IQueryable<TSource> source, Expression<Func<TSource,TTarget>> target, Expression<Func<TSource,TTarget>> setter) // var body = ((LambdaExpression)expr).Body; int level = body.GetLevel(); var tableInfo = sequence.IsExpression(body, level, RequestFor.Table); if (tableInfo.Result == false) { throw new LinqException("Expression '{0}' must be a table."); } into = tableInfo.Context; } else { // static int Update<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter) // into = builder.BuildSequence(new BuildInfo(buildInfo, expr, new SelectQuery())); } sequence.ConvertToIndex(null, 0, ConvertFlags.All); new SelectQueryOptimizer(builder.DataContext.SqlProviderFlags, sequence.SelectQuery) .ResolveWeakJoins(new List <ISqlTableSource>()); sequence.SelectQuery.Select.Columns.Clear(); BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), into, sequence.SelectQuery.Update.Items, sequence); var sql = sequence.SelectQuery; sql.Select.Columns.Clear(); foreach (var item in sql.Update.Items) { sql.Select.Columns.Add(new SqlColumn(sql, item.Expression)); } sql.Update.Table = ((TableBuilder.TableContext)into).SqlTable; } break; } } sequence.SelectQuery.ChangeQueryType(QueryType.Update); return(new UpdateContext(buildInfo.Parent, sequence)); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var statement = mergeContext.Merge; if (methodCall.Arguments.Count == 2) { // On<TTarget, TSource>(IMergeableOn<TTarget, TSource> merge, Expression<Func<TTarget, TSource, bool>> matchCondition) var predicate = methodCall.Arguments[1]; var condition = (LambdaExpression)predicate.Unwrap(); var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap()); mergeContext.AddTargetParameter(condition.Parameters[0]); mergeContext.AddSourceParameter(condition.Parameters[1]); var filterExpression = BuildSearchCondition(builder, statement, mergeContext.TargetContext, mergeContext.SourceContext, condition); statement.On.Conditions.AddRange(filterExpression.Conditions); } else if (methodCall.Arguments.Count == 3) { var targetKeyLambda = ((LambdaExpression)methodCall.Arguments[1].Unwrap()); var sourceKeyLambda = ((LambdaExpression)methodCall.Arguments[2].Unwrap()); var targetKeySelector = targetKeyLambda.Body.Unwrap(); var sourceKeySelector = sourceKeyLambda.Body.Unwrap(); var targetKeyContext = new ExpressionContext(buildInfo.Parent, mergeContext.TargetContext, targetKeyLambda); var sourceKeyContext = new ExpressionContext(buildInfo.Parent, mergeContext.SourceContext, sourceKeyLambda); if (targetKeySelector.NodeType == ExpressionType.New) { var new1 = (NewExpression)targetKeySelector; var new2 = (NewExpression)sourceKeySelector; for (var i = 0; i < new1.Arguments.Count; i++) { var arg1 = new1.Arguments[i]; var arg2 = new2.Arguments[i]; JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, arg1, sourceKeyContext, arg2); } } else if (targetKeySelector.NodeType == ExpressionType.MemberInit) { // TODO: migrate unordered members support to original code var mi1 = (MemberInitExpression)targetKeySelector; var mi2 = (MemberInitExpression)sourceKeySelector; if (mi1.Bindings.Count != mi2.Bindings.Count) { throw new LinqException($"List of member inits does not match for entity type '{targetKeySelector.Type}'."); } for (var i = 0; i < mi1.Bindings.Count; i++) { var binding2 = (MemberAssignment)mi2.Bindings.Where(b => b.Member == mi1.Bindings[i].Member).FirstOrDefault(); if (binding2 == null) { throw new LinqException($"List of member inits does not match for entity type '{targetKeySelector.Type}'."); } var arg1 = ((MemberAssignment)mi1.Bindings[i]).Expression; var arg2 = binding2.Expression; JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, arg1, sourceKeyContext, arg2); } } else { JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, targetKeySelector, sourceKeyContext, sourceKeySelector); } } else { // OnTargetKey<TTarget>(IMergeableOn<TTarget, TTarget> merge) var targetType = statement.Target.SystemType !; var pTarget = Expression.Parameter(targetType, "t"); var pSource = Expression.Parameter(targetType, "s"); var targetDescriptor = builder.MappingSchema.GetEntityDescriptor(targetType); Expression?ex = null; for (var i = 0; i < targetDescriptor.Columns.Count; i++) { var column = targetDescriptor.Columns[i]; if (!column.IsPrimaryKey) { continue; } var expr = Expression.Equal( Expression.MakeMemberAccess(pTarget, column.MemberInfo), Expression.MakeMemberAccess(pSource, column.MemberInfo)); ex = ex != null?Expression.AndAlso(ex, expr) : expr; } if (ex == null) { throw new LinqToDBException("Method OnTargetKey() needs at least one primary key column"); } var condition = Expression.Lambda(ex, pTarget, pSource); var filterExpression = BuildSearchCondition(builder, statement, mergeContext.TargetContext, mergeContext.SourceContext, condition); statement.On.Conditions.AddRange(filterExpression.Conditions); } mergeContext.SourceContext.MatchBuilt(); return(mergeContext); }
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, 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, outerKeyContext, arg1, innerKeyContext, arg2); } } else { BuildJoin(builder, join, 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 ); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var updateStatement = sequence.Statement as SqlUpdateStatement ?? new SqlUpdateStatement(sequence.SelectQuery); sequence.Statement = updateStatement; switch (methodCall.Arguments.Count) { case 1: // int Update<T>(this IUpdateable<T> source) { CheckAssociation(sequence); break; } case 2: // int Update<T>(this IQueryable<T> source, Expression<Func<T,T>> setter) { CheckAssociation(sequence); if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty) { sequence = new SubQueryContext(sequence); updateStatement.SelectQuery = sequence.SelectQuery; sequence.Statement = updateStatement; } BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[1].Unwrap(), sequence, updateStatement.Update.Items, sequence); break; } case 3: { var expr = methodCall.Arguments[1].Unwrap(); if (expr is LambdaExpression lex && lex.ReturnType == typeof(bool)) { CheckAssociation(sequence); // int Update<T>(this IQueryable<T> source, Expression<Func<T,bool>> predicate, Expression<Func<T,T>> setter) // sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false); if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty) { sequence = new SubQueryContext(sequence); } updateStatement.SelectQuery = sequence.SelectQuery; sequence.Statement = updateStatement; BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[2].Unwrap(), sequence, updateStatement.Update.Items, sequence); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); JoinType joinType; var conditionIndex = 2; switch (methodCall.Method.Name) { case "InnerJoin": joinType = JoinType.Inner; break; case "CrossJoin": joinType = JoinType.Inner; conditionIndex = -1; break; case "LeftJoin": joinType = JoinType.Left; break; case "RightJoin": joinType = JoinType.Right; break; case "FullJoin": joinType = JoinType.Full; break; default: conditionIndex = 3; joinType = (SqlJoinType)methodCall.Arguments[2].EvaluateExpression() !switch { SqlJoinType.Inner => JoinType.Inner, SqlJoinType.Left => JoinType.Left, SqlJoinType.Right => JoinType.Right, SqlJoinType.Full => JoinType.Full, _ => throw new InvalidOperationException($"Unexpected join type: {(SqlJoinType)methodCall.Arguments[2].EvaluateExpression()!}") }; break; } if (joinType == JoinType.Right || joinType == JoinType.Full) { outerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, outerContext, null); } outerContext = new SubQueryContext(outerContext); if (joinType == JoinType.Left || joinType == JoinType.Full) { innerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, innerContext, null); } innerContext = new SubQueryContext(innerContext); var selector = (LambdaExpression)methodCall.Arguments[methodCall.Arguments.Count - 1].Unwrap(); outerContext.SetAlias(selector.Parameters[0].Name); innerContext.SetAlias(selector.Parameters[1].Name); var joinContext = new JoinContext(buildInfo.Parent, selector, outerContext, innerContext) #if DEBUG { MethodCall = methodCall } #endif ; if (conditionIndex != -1) { var condition = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap(); var conditionExpr = condition.GetBody(selector.Parameters[0], selector.Parameters[1]); conditionExpr = builder.ConvertExpression(conditionExpr); var join = new SqlFromClause.Join(joinType, innerContext.SelectQuery, null, false, Array <SqlFromClause.Join> .Empty); outerContext.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); builder.BuildSearchCondition( joinContext, conditionExpr, @join.JoinedTable.Condition.Conditions); } else { outerContext.SelectQuery.From.Table(innerContext.SelectQuery); } return(joinContext); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var insertOrUpdateStatement = new SqlInsertOrUpdateStatement(sequence.SelectQuery); sequence.Statement = insertOrUpdateStatement; UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[1].Unwrap(), sequence, insertOrUpdateStatement.Insert.Items, sequence); var updateExpr = methodCall.Arguments[2].Unwrap(); if (!(updateExpr is ConstantExpression constant && constant.Value == null)) { UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)updateExpr, sequence, insertOrUpdateStatement.Update.Items, sequence); } insertOrUpdateStatement.Insert.Into = ((TableBuilder.TableContext)sequence).SqlTable; insertOrUpdateStatement.Update.Table = ((TableBuilder.TableContext)sequence).SqlTable; insertOrUpdateStatement.SelectQuery.From.Tables.Clear(); insertOrUpdateStatement.SelectQuery.From.Table(insertOrUpdateStatement.Update.Table); if (methodCall.Arguments.Count == 3) { var table = insertOrUpdateStatement.Insert.Into; var keys = table.GetKeys(false); if (keys.Count == 0) { throw new LinqException("InsertOrUpdate method requires the '{0}' table to have a primary key.", table.Name); } var q = ( from k in keys join i in insertOrUpdateStatement.Insert.Items on k equals i.Column select new { k, i } ).ToList(); var missedKey = keys.Except(q.Select(i => i.k)).FirstOrDefault(); if (missedKey != null) { throw new LinqException("InsertOrUpdate method requires the '{0}.{1}' field to be included in the insert setter.", table.Name, ((SqlField)missedKey).Name); } insertOrUpdateStatement.Update.Keys.AddRange(q.Select(i => i.i)); } else { UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)methodCall.Arguments[3].Unwrap(), sequence, insertOrUpdateStatement.Update.Keys, sequence); } return(new InsertOrUpdateContext(buildInfo.Parent, sequence)); }
public IBuildContext BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) { var call = (MethodCallExpression)buildInfo.Expression; return(new Context(builder.BuildSequence(new BuildInfo(buildInfo, call.Arguments[0])))); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0], buildInfo.SelectQuery)); var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); outerContext = new SubQueryContext(outerContext); innerContext = new SubQueryContext(innerContext); var selector = (LambdaExpression)methodCall.Arguments[methodCall.Arguments.Count - 1].Unwrap(); outerContext.SetAlias(selector.Parameters[0].Name); innerContext.SetAlias(selector.Parameters[1].Name); JoinType joinType; var conditionIndex = 2; switch (methodCall.Method.Name) { case "InnerJoin": joinType = JoinType.Inner; break; case "CrossJoin": joinType = JoinType.Inner; conditionIndex = -1; break; case "LeftJoin": joinType = JoinType.Left; break; case "RightJoin": joinType = JoinType.Right; break; case "FullJoin": joinType = JoinType.Full; break; default: conditionIndex = 3; var joinValue = (SqlJoinType)methodCall.Arguments[2].EvaluateExpression(); switch (joinValue) { case SqlJoinType.Inner: joinType = JoinType.Inner; break; case SqlJoinType.Left: joinType = JoinType.Left; break; case SqlJoinType.Right: joinType = JoinType.Right; break; case SqlJoinType.Full: joinType = JoinType.Full; break; default: throw new ArgumentOutOfRangeException(); } break; } if (conditionIndex != -1) { var condition = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap(); var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap()); var join = new SqlFromClause.Join(joinType, innerContext.SelectQuery, null, false, Array <SqlFromClause.Join> .Empty); outerContext.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); builder.BuildSearchCondition( new ExpressionContext(null, new[] { outerContext, innerContext }, condition), conditionExpr, join.JoinedTable.Condition.Conditions); } else { outerContext.SelectQuery.From.Table(innerContext.SelectQuery); } return(new SelectContext(buildInfo.Parent, selector, outerContext, innerContext) #if DEBUG { MethodCall = methodCall } #endif ); }