Skip to content

fix(filter): resolve non-public members like v1.14.2 - #155

Merged
pdevito3 merged 2 commits into
mainfrom
fm/qk-restore-d1-d5-d1
Oct 1, 2026
Merged

pdevito3 merged 2 commits into
mainfrom
fm/qk-restore-d1-d5-d1

Conversation

@pdevito3

@pdevito3 pdevito3 commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

This PR restores D1 from the v1.14.2 review report. It is not a breaking change.

v1.14.2 resolved each filter segment with Expression.PropertyOrField. Main resolves only public members. As a result, main rejects filters on internal, protected, and private members that v1.14.2 accepted. Main also treats an indexer (Item) as a member and throws ArgumentException.

This PR makes PropertyResolver use the lookup order of Expression.PropertyOrField, ignoring case:

  1. A public property
  2. A public field
  3. A non-public property
  4. A non-public field

An indexer does not match. It is an unknown property again.

Results against v1.14.2

Input v1.14.2 main This PR
InternalScore > 30 (internal property) x.InternalScore > 30 UnknownFilterPropertyException same as v1.14.2
secretRank == 7 (private field) x.secretRank == 7 UnknownFilterPropertyException same as v1.14.2
ProtectedNote == "a" filters throws same as v1.14.2
Author.InternalAlias == "Ann" filters throws same as v1.14.2
secretRank > 100 with AllowUnknownProperties x.secretRank > 100 True == True same as v1.14.2
Item == "x" (indexer) UnknownFilterPropertyException 'Item' ArgumentException same as v1.14.2
Item == "x" with AllowUnknownProperties True == True ArgumentException same as v1.14.2

A differential probe ran 23 cases against v1.14.2 and this branch, on the expression, the in-memory rows, and the Postgres SQL. The only remaining difference is the in-list constant type for InternalScore ^^ [40, 50] (List against InListValues). PR #133 made that change on purpose, and it does not change the result or the SQL.

Tests

  • Unit pins in QueryKit.UnitTests/PropertyResolverTests.cs: internal, protected, private, and nested internal members, a property list, a query name on an internal member, AllowUnknownProperties, the public-before-non-public order, and the indexer. 13 of these fail on main.
  • Postgres pins in QueryKit.IntegrationTests/Tests/PropertyResolverTests.cs: a filter on an internal EF-mapped property, with and without AllowUnknownProperties. Both fail on main with UnknownFilterPropertyException.
  • The test model gets an internal Nickname property on TestingPerson, mapped to a new nullable nickname column with a migration. QueryKit.WebApiTestProject makes its internals visible to the integration tests.
  • dotnet test: unit 416 passed, integration 285 passed.

Overlap with other PRs

Restore the v1.14.2 member lookup of Expression.PropertyOrField. A filter segment matches a public property, a public field, a non-public property, and then a non-public field, in any case. An indexer is an unknown property again, so it throws UnknownFilterPropertyException, or it becomes a True clause when AllowUnknownProperties is on.

The test model gets an internal mapped Nickname column on people, so that a Postgres test can pin the SQL path.
@pdevito3
pdevito3 merged commit ed8188d into main Oct 1, 2026
2 checks passed
@pdevito3
pdevito3 deleted the fm/qk-restore-d1-d5-d1 branch October 1, 2026 20:39
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