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.In(SetOperation.ExceptAll, SetOperation.IntersectAll) || !builder.DataContext.SqlProviderFlags.IsDistinctSetOperationsSupported && setOperation.In(SetOperation.Except, SetOperation.Intersect); if (needsEmulation) { // emulation var sequence = new SubQueryContext(sequence1); var query = sequence2; var except = query.SelectQuery; var sql = sequence.SelectQuery; if (setOperation.In(SetOperation.Except, SetOperation.Intersect)) { sql.Select.IsDistinct = true; } except.ParentSelect = sql; if (setOperation.In(SetOperation.Except, 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 SetOperationContext(SubQueryContext sequence1, SubQueryContext sequence2, MethodCallExpression methodCall, SqlSetOperator setOperator) : base(sequence1) { _isObject = sequence1.IsExpression(null, 0, RequestFor.Object).Result || sequence2.IsExpression(null, 0, RequestFor.Object).Result; if (_isObject) { _type = methodCall.Method.GetGenericArguments()[0]; _unionParameter = Expression.Parameter(_type, "t"); } // initial sequences AddSequence(sequence1, null); AddSequence(sequence2, setOperator); }