fix: close LINQ Where predicate-translation gaps#124
Merged
Conversation
Negation (`!`) in a Where predicate was silently dropped, producing the opposite filter. Override VisitUnary to wrap the operand in a `not.` filter, and fix PrepareFilter to serialize negated groups as `not.and=(...)` / `not.or=(...)`.
`Where(x => ids.Contains(x.Col))` previously threw instead of producing a Postgrest `in` filter. VisitMethodCall now classifies Contains by which side references the model: a column containing a constant keeps the cs/like translation, while a captured collection containing a column becomes `in`. Handles instance, static Enumerable, and span-based MemoryExtensions.Contains; ambiguous shapes throw a descriptive error.
`Where(x => x.IsActive)` and `Where(x => !x.IsActive)` threw "Unable to parse the supplied predicate" because the visitor only built filters from binary and method-call expressions. VisitMember now translates a boolean column that references the model into `column.eq.true`, working as a bare body, inside &&/|| groups, and negated (via the existing not. wrapping).
`Where(x => x.StartDate < x.EndDate)` crashed with a cryptic "variable 'x' ... not defined" because the visitor tried to evaluate the right-hand column as a local value. VisitBinary now detects a right side that references the model and throws an ArgumentException explaining that Postgrest has no column-to-column filter, suggesting a computed column or RPC instead.
spydon
approved these changes
Jul 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #122.
WhereExpressionVisitormishandled several common LINQWherepredicate shapes: one produced incorrect results, two threw non-descriptive errors, and one was unsupported. Each is addressed in its own commit, developed test-first againstGenerateUrl()/exception assertions, with theWhereXML documentation updated to describe supported and unsupported shapes.Where(x => !(x.Name == "foo"))generatedname=eq.foo— the inverse of the intended filter — which also affectedUpdate/Deletecalls using the same predicate. Negation is now translated to anot.filter.PrepareFilteris also corrected so a negated logical group serializes asnot.and=(...), which additionally fixes the public.Not()API for group filters.Containsis translated toin.Where(x => ids.Contains(x.Id))previously threw rather than producing aninfilter. InstanceList.Contains, staticEnumerable.Contains, and the span-basedMemoryExtensions.Containsused for arrays on modern .NET are all supported.Where(x => x.IsActive)andWhere(x => !x.IsActive)previously threw "Unable to parse"; they now translate toeqandnot.eq.Where(x => x.Start < x.End)previously failed withvariable 'x' not defined. PostgREST filters compare a column to a literal value and cannot express a column-to-column comparison, so this now throws anArgumentExceptionrecommending a computed/generated column or an RPC.All new tests assert on
GenerateUrl()and require no network access; the full LINQ unit suite and theTestLinqWhereintegration test pass.