Skip to content

fix(filter)!: resolve a child collection member in any case - #139

Open
pdevito3 wants to merge 1 commit into
mainfrom
fm/qk-breaking-child-collection-case
Open

pdevito3 wants to merge 1 commit into
mainfrom
fm/qk-breaking-child-collection-case

Conversation

@pdevito3

@pdevito3 pdevito3 commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

For later consideration in a major version. Do not merge now. #134 restored the v1.14.2 behavior to keep v1.x compatible (restore commit 0f1d864). This PR re-applies b562d00 (#113). The captain decides on this PR separately.

Summary

PropertyResolver.ResolveMemberPath resolves each segment after a collection like every other segment: it ignores the letter case, and a segment that does not match is an unknown property.

v1.14.2 behavior (and main)

  • The first member after a collection must match in the exact case. Later segments ignore the case.
  • A first member after a collection that does not match throws NullReferenceException. AllowUnknownProperties does not change this.

New behavior

  • Every segment ignores the case, also after a collection.
  • A member after a collection that does not match is an unknown property. It throws UnknownFilterPropertyException, or the clause is ignored when AllowUnknownProperties is on, like an unknown property in any other place.

Example

FilterParser.ParseFilter<Recipe>("""ingredients.name == "flour" """);
  • v1.14.2 and main: throws NullReferenceException.
  • This PR: x => x.Ingredients.Select(y => y.Name).Any(z => (z == "flour")).

Justification

The case rule after a collection was not documented, and it did not match the rest of the path. A NullReferenceException does not tell the caller what is wrong. It also skips AllowUnknownProperties.

Migration

A caller that catches NullReferenceException for this input must catch UnknownFilterPropertyException, or set AllowUnknownProperties.

README

No change. The README does not document the case rule for child collections.

Tests

These tests come back from main before #134, in unit FilterParserTests:

  • child_collection_member_in_another_case_throws -> child_collection_member_resolves_in_any_case
  • The pins unknown_child_collection_member_throws_when_unknown_properties_are_allowed, member_after_a_child_collection_member_resolves_in_any_case, and nested_child_collection_member_in_another_case_throws are removed. They held the v1.14.2 behavior.

New integration test in DatabaseFilteringTests (Postgres): can_filter_by_child_collection_member_in_another_case.

dotnet test: 467 unit tests and 298 Postgres integration tests (Testcontainers) pass, 0 failures.

Rebase on main

This branch is rebased on current main. On main, a segment that is not after a collection matches members in the order of Expression.PropertyOrField: a public property, a public field, a non-public property, and then a non-public field. With this PR, a segment after a collection uses the same order. As a result, a non-public member after a collection also resolves. #161 limits every segment to public members.

The first member after a collection had to match in the exact case, and a member that did not match threw NullReferenceException. Every other segment of a property path ignores the case. Resolve the members after a collection like every other segment.

BREAKING CHANGE: a child collection member in another case, for example ingredients.name, now filters by the member. An unknown member after a collection now follows AllowUnknownProperties and no longer throws NullReferenceException.
@pdevito3
pdevito3 force-pushed the fm/qk-breaking-child-collection-case branch from f34cc9a to c4a474f Compare October 1, 2026 21:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant