{"record":{"id":"1d6c332af472d48a","repo":"dotnet/efcore","slug":"unable-to-translate-set-operation-after-client-pro","errorCode":null,"errorMessage":"Unable to translate set operation after client projection has been applied. Consider moving the set operation before the last 'Select' call.","messagePattern":"Unable to translate set operation after client projection has been applied\\. Consider moving the set operation before the last 'Select' call\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs","lineNumber":446,"sourceCode":"                        NewArrayInit(\n                            typeof(object),\n                            source1SelectorExpressions.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e))),\n                    CurrentParameter));\n\n            source2.ServerQueryExpression = Call(\n                EnumerableMethods.Select.MakeGenericMethod(source2.ServerQueryExpression.Type.GetSequenceType(), typeof(ValueBuffer)),\n                source2.ServerQueryExpression,\n                Lambda(\n                    New(\n                        ValueBufferConstructor,\n                        NewArrayInit(\n                            typeof(object),\n                            source2SelectorExpressions.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e))),\n                    source2.CurrentParameter));\n        }\n        else\n        {\n            throw new InvalidOperationException(InMemoryStrings.SetOperationsNotAllowedAfterClientEvaluation);\n        }\n\n        ServerQueryExpression = Call(\n            setOperationMethodInfo.MakeGenericMethod(typeof(ValueBuffer)), ServerQueryExpression, source2.ServerQueryExpression);\n    }\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    public virtual void ApplyDefaultIfEmpty()\n    {\n        if (_clientProjections.Count != 0)\n        {\n            throw new InvalidOperationException(InMemoryStrings.DefaultIfEmptyAppliedAfterProjection);\n        }","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs#L428-L464","documentation":"The InMemory provider implements set operations (Union, Intersect, Except) by composing two server-side ValueBuffer streams. If client projections have already been applied to either side (i.e. _clientProjections is non-empty on a source), the provider can no longer merge at the server level and throws SetOperationsNotAllowedAfterClientEvaluation. The else-branch that throws is reached when at least one source has client projections that cannot be lifted.","triggerScenarios":"Calling a set operation (Union/Intersect/Except/Concat) after a Select that caused client-side projection evaluation in the InMemory provider. The throw happens during ApplySetOperation in InMemoryQueryExpression.","commonSituations":"Building a query like ctx.Blogs.Select(...complex...).Union(other) where the Select includes a non-translatable expression, or chaining a set operation after a projection that materializes a subquery. Moving from SQL Server (which can push the set op down) to InMemory tests and hitting the limitation.","solutions":["Move the set operation (Union/Intersect/Except) before the last Select call so both operands are still server-translatable.","Insert AsEnumerable() before the set operation to perform the union/intersect on the client side after materialization.","Ensure both operands of the set operation project the same simple, fully-translatable shape before combining."],"exampleFix":"// before — set op after client-projection Select\nvar query = ctx.Blogs\n    .Select(b => new { b.Id, Label = CustomLabel(b) })\n    .Union(ctx.Posts.Select(p => new { p.Id, Label = CustomLabel(p) }));\n\n// after — move set op before the Select that needs client eval\nvar query = ctx.Blogs\n    .Select(b => new { b.Id, Label = b.Title })\n    .Union(ctx.Posts.Select(p => new { p.Id, Label = p.Title }))\n    .AsEnumerable()\n    .Select(x => new { x.Id, Label = CustomLabel(x) });","handlingStrategy":"validation","validationCode":"// Validate query structure: ensure set operations (Union/Intersect/Except)\n// appear before the last Select. If a Select must do client-side work,\n// move it after AsEnumerable.\n// Rule: no Select with client-side logic between the set op and the source.\n// In practice, structure queries so both operands are fully translatable:\nvar part1 = ctx.Blogs.Select(b => new { b.Id, Key = b.Title });\nvar part2 = ctx.Posts.Select(p => new { p.Id, Key = p.Title });\nvar union = part1.Union(part2); // safe — both sides translatable","typeGuard":null,"tryCatchPattern":"try\n{\n    return query.ToList();\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"set operation after client projection\"))\n{\n    // Move the set operation before the client Select, or use AsEnumerable\n    var serverUnion = part1.Union(part2);\n    return serverUnion.AsEnumerable().Select(clientProject).ToList();\n}","preventionTips":["Place Union/Intersect/Except before any Select that might cause client evaluation.","Ensure both operands of a set operation project identical, translatable shapes.","If client-side projection is needed, insert AsEnumerable() after the set operation.","Test set-operation queries against InMemory provider in integration tests."],"tags":["efcore","inmemory","query-translation","set-operations","client-eval"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}