Repository navigation
Conversation
`new[] { "a", "b" }.Contains(x.Name)` failed to parse with "Unable to find
any queryable members in expression new [] {"a", "b"}", and an inline array
of captured values, `new[] { id1, id2 }.Contains(x.Id)`, with "Marten does
not (yet) support member Guid.id1". The same values in a captured local
array, or passed to IsOneOf(), have always worked.
The Contains parsers (EnumerableContains, and MemoryExtensionsContains for
the C# 14 span binding) decide which side is the value through
TryToParseConstant, which accepted only a ConstantExpression or a closed
MemberExpression. An array literal is a NewArrayInit, so it fell through to
member resolution. ReduceToConstant already evaluates NewArrayInit, so
TryToParseConstant now accepts one whose elements read no lambda parameter.
An array assembled from a compiled query's own members is left alone. The
plan binds each parameter to one of the query's members by matching values,
an array of several members matches none of them, and reducing it would
freeze the planning-time values into every later execution. That shape keeps
failing the way it did before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 19, 2026
Member
|
Thanks @lahma — this is superseded by #5712, which carries your commit unchanged (you're its author, and co-author on the follow-up). #5712 adds three things on top:
Closing this in favour of #5712. Generated by Claude Code |
jeremydmiller
added a commit
that referenced
this pull request
Oct 19, 2026
Treat an inline array literal as a value in LINQ Contains(), including a compiled query's own members (supersedes #5691)
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.
What fails
throws while the query is parsed:
The same happens with int, Guid and enum literals (both
EnumStoragemodes), with the static formEnumerable.Contains(new[] { … }, x.Name), with the negated form, and against a duplicated field. An inline array of captured values,new[] { id1, id2 }.Contains(x.Id), fails with a misleading message:Marten does not (yet) support member Guid.id1.The same values work in a captured local array, and when passed to
IsOneOf(). The AOT publishing guide already lists array literals among the values Marten evaluates in a where clause.Why
Two
Containsparsers see this shape.EnumerableContainshandles it under C# 13 (net9.0) and for the static form.MemoryExtensionsContainshandles C# 14's span binding (net10.0). Both decide which side is the value withTryToParseConstant, which accepted only aConstantExpressionor a closedMemberExpression. An array literal is aNewArrayInit, so it fell through toMemberFor. That looks for a document member, finds none, or, for captured elements, takes the closure field for one.ReduceToConstantalready evaluatesNewArrayInit, reflectively for constant and captured elements (#5328), so only the recognition was missing.IsOneOf()works because it callsReduceToConstantdirectly.The change
TryToParseConstantalso accepts aNewArrayInitwhose elements read no lambda parameter, and reduces it withReduceToConstant. An array that reads the document, such asnew[] { x.A, x.B }.Contains("c"), is not a value and fails as before.One shape is left out on purpose: an array built from a compiled query's own members,
new[] { First, Second }.Contains(x.Name). A compiled query plan binds each parameter to one of the query's members by matching values, and an array of two members matches neither. Reducing the literal would bake the planning-time values into every later execution. I checked: without this guard, a second call with different values returns the first call's rows. So an array that reads a compiled query instance (anICompiledQueryMarkerconstant) is not reduced, and that shape keeps failing as before. An array literal of constants inside a compiled query works now.A collection initializer,
new List<T> { … }.Contains(…), is aListInitrather than aNewArrayInitand is not covered here; it still fails as before.The other callers of
TryToParseConstant(Any(), SimpleExpression, the GroupBy HAVING operand, modulo,string.IsNullOrEmpty, dictionary values) either already reduced this shape throughReduceToConstantor now get a value where they used to throw.The same value-matching gap already affects other compiled query shapes on master, and this PR does not change them:
x.Name.IsOneOf(new[] { First, Second })andx.Name == First + Secondboth keep their planning-time values.Tests
LinqTests/Bugs/Bug_inline_array_literal_as_contains_source.cs:string_array_literalnew[] { "Red", "Blue" }.Contains(x.Name)string_array_literal_through_static_enumerable_containsEnumerable.Contains(new[] { … }, x.Name), which pins the Enumerable parser on net10.0negated_string_array_literal!new[] { … }.Contains(x.Name)int_array_literalnew[] { 1, 3 }guid_array_literalnew[] { new Guid("…"), new Guid("…") }array_literal_of_captured_valuesnew[] { red, blue }.Contains(x.Id)enum_array_literal(AsInteger / AsString)new[] { Shade.Light, Shade.Dark }string_array_literal_against_a_duplicated_fieldDuplicate(x => x.Name); the SQL readsd.namecompiled_query_with_an_array_literalcompiled_query_still_refuses_an_array_literal_of_its_own_membersThe instance form binds to
Enumerable.Containson net9.0 and toMemoryExtensions.Containson net10.0, so running both target frameworks covers both parsers.Without the fix, 10 of 11 fail on net9.0 and on net10.0. With the fix, 11 of 11 pass on both. The full LinqTests suite passes on both: 1642/1642 on net9.0 and 1642/1642 on net10.0. CompiledQueryTests passes on both as well.
🤖 Generated with Claude Code