Skip to content

fix: honor HasConversion when a property has a HasQueryName alias - #107

Merged
pdevito3 merged 1 commit into
mainfrom
fm/qk-alias-conversion-struct
Sep 29, 2026
Merged

pdevito3 merged 1 commit into
mainfrom
fm/qk-alias-conversion-struct

Conversation

@pdevito3

@pdevito3 pdevito3 commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes #106. When a property has HasQueryName and HasConversion, a filter on the alias ignored the conversion. For a struct, the parser threw Unsupported value. In other cases, the filter returned the wrong rows.

Root cause

ParseFilter replaces each query name with its property path before it parses. The conversion lookups in FilterParser then called GetPropertyInfoByQueryName with this property path. When the alias and the path differed by more than case, the lookup missed the configuration.

The fault is not specific to value types. The reference-type example in the issue worked only because email and Email differ by case alone. An alias such as mail on a reference type failed too.

Changes

All changes are in QueryKit/FilterParser.cs:

  • CreateRightExpr and the two lookups in the member expression builder use GetPropertyInfo(propertyPath).
  • A null literal on a converted property compares against a typed null. Before, the parser built new EmailAddress("null") and returned no rows. With an alias, it threw.
  • A converted Nullable<T> struct is built from its underlying type, then converted to T?.
  • Guid string operators (for example @=) pass the converted string expression to CreateRightExpr. Before, a Guid with HasConversion<string>() threw Expression of type 'System.Guid' cannot be used for parameter of type 'System.String'.
  • Property lists (for example (id) == "2") use the resolved member path for the conversion lookup. As a result, a lowercase path still finds the conversion.

Tests

  • New QueryKit.UnitTests/HasConversionTests.cs (17 tests) covers structs, reference types, nested properties, a child of a converted parent, property lists, nullable structs, null literals, and Guid string operators. Most cases run with and without an alias.
  • 7 new Postgres tests in QueryKit.IntegrationTests/Tests/HasConversionTests.cs cover an alias on Email, the property path when an alias is set, Email.Value with an alias, null with an alias, the owned PhysicalAddress.PostalCode with and without an alias, and Guid @= with an alias.
  • On main, 11 of the new unit tests and 5 of the new integration tests fail. The other new tests are guards for cases that already work.
  • Full suites on this branch: unit 204 passed, 2 skipped. Integration 212 passed, 1 skipped.

End-to-end check

Each filter ran through ApplyQueryKitFilter on an in-memory list and on Postgres through EF Core 10. The model has a converted struct Code, a converted record Contact, and a Guid Id. In every case, the in-memory list and Postgres gave the same result.

Filter Configuration main This branch
sku == "BEEF-2" alias sku on Code Unsupported value 'BEEF-2' for type 'RecipeCode' 1 row, WHERE r."Code" = 'BEEF-2'
sku != "BEEF-2" alias sku on Code same error 3 rows, WHERE r."Code" <> 'BEEF-2'
Code == "BEEF-2" alias sku on Code same error 1 row
Code == "BEEF-2" no alias 1 row 1 row
mail == "julia@example.com" alias mail on Contact Unsupported value 'julia@example.com' for type 'ChefEmail' 2 rows, WHERE r."Contact" = 'julia@example.com'
mail == null alias mail on Contact Unsupported value 'null' for type 'ChefEmail' 1 row, WHERE r."Contact" IS NULL
Contact == null no alias 0 rows, WHERE r."Contact" = 'null' 1 row, WHERE r."Contact" IS NULL
identifier @= "000000000002" alias identifier on Id 1 row 1 row
Id @= "000000000002" no alias ArgumentException 1 row, WHERE r."Id"::text LIKE '%000000000002%'

Out of scope

This change does not affect these separate, older faults:

  • Aliases inside property lists: (wrappedid) == "2" throws UnknownFilterPropertyException. The alias rewrite needs an operator after the name.
  • Conversions on collection element paths (for example Items.Select(i => i.Contact)): no conversion lookup occurs, with or without an alias.

The filter parser replaces a query name with the property path before it parses. The HasConversion lookups then searched by query name, so they missed the configuration. The filter then failed or returned the wrong rows.

The conversion lookups now use the property path. The fix also corrects four related faults in the same code path:
- A null literal on a converted property compares against null. Before, the parser built a value from the text "null".
- A converted Nullable<T> struct is built from its underlying type.
- Guid string operators pass the string expression to the right side.
- Property lists use the resolved member path, so a lowercase path still finds the conversion.

Closes #106
@pdevito3
pdevito3 merged commit b817eb3 into main Sep 29, 2026
2 checks passed
@pdevito3
pdevito3 deleted the fm/qk-alias-conversion-struct branch September 29, 2026 17:47
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.

HasQueryName alias silently drops HasConversion for value-type (struct) properties

1 participant