fix(query): honor root-level OrderExpression.EntityName for linked-entity sorting#43
Merged
Conversation
…tity sorting
Root-level OrderExpression entries with a non-null EntityName (produced by FetchXML
`<order entityname="alias" attribute="..." />` or set directly on qe.Orders) were
silently ignored. CollectOrders passed alias=null for all root orders, so the key
resolver looked up the bare attribute name instead of "{alias}.{attribute}", returning
rows in an unordered sequence and causing wrong results when combined with TopCount.
- XDocumentExtensions.ToOrderExpressionList: read the `entityname` XML attribute and
populate OrderExpression.EntityName
- QueryProcessor.ConvertXmlDocumentToQueryExpression: add orders via query.Orders.Add
instead of AddOrder so EntityName is not discarded
- QueryProcessor.CollectOrders: use order.EntityName as the alias when set
Qodana Community for .NETIt seems all right 👌 No new problems were found according to the checks applied View the detailed Qodana reportTo be able to view the detailed Qodana report, you can either:
To get - name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.1.0
with:
upload-result: trueContact Qodana teamContact us at qodana-support@jetbrains.com
|
PepperPix
pushed a commit
that referenced
this pull request
Jun 19, 2026
# [1.1.0-beta.1](v1.0.0...v1.1.0-beta.1) (2026-06-19) ### Bug Fixes * **bulk-delete:** copy RecurrencePattern and StartDateTime to asyncoperation ([3cffda7](3cffda7)) * **bulk-delete:** omit recurrencepattern and recurrencestarttime when not set ([0c0a2e8](0c0a2e8)) * **bulk-delete:** populate name, ownerid, and operationtype on asyncoperation entity ([2ac6f28](2ac6f28)) * deep-clone OptionSetValue instances inside OptionSetValueCollection ([dd72956](dd72956)) * enforce single-child and entity type validation for N:1 deep insert ([7ae25aa](7ae25aa)) * ensure CloneEntity returns base Entity runtime type ([f393387](f393387)) * fall back to EntityLogicalNameAttribute in CreateQuery<T> when type is not in resolver cache ([5ba7140](5ba7140)) * merge attributes on Update instead of replacing the entire entity ([753ecd6](753ecd6)) * **query:** clone outer entity per row in LeftOuter join ([501db49](501db49)) * **query:** fix 4 critical query engine bugs ([ef0e7e4](ef0e7e4)) * **query:** guard against null Attributes array in PatchDateFormat ([3021776](3021776)) * **query:** handle null otherEntity in JoinAttributes for left outer joins ([a7c67cb](a7c67cb)) * **query:** honor LinkEntity.Orders in RetrieveMultiple ordering ([#42](#42)) ([4b363b1](4b363b1)) * **query:** honor root-level OrderExpression.EntityName for linked-entity sorting ([#43](#43)) ([102de5d](102de5d)) * **query:** match primary id attribute against Entity.Id in RetrieveMultiple ([5853fb6](5853fb6)) * **query:** materialize DateTime attributes before modifying collection in PatchDateFormat ([dd4d2e3](dd4d2e3)) * **query:** pass Guid directly for EqualUserId/EqualBusinessId conditions ([e7b8c77](e7b8c77)) * **query:** treat fetchxml bool condition values without metadata as bool ([8be757e](8be757e)) * **query:** use fiscalYear from condition as year for InFiscalYear date range ([e4677e6](e4677e6)) * **query:** use TimeOnly.MaxValue for end-of-day upper bound in date range operators ([49b1679](49b1679)) * **query:** validate actual values in aggregate FetchXml guards ([fe9cfcf](fe9cfcf)) * remove 14 redundant using directives ([1771050](1771050)) * remove 5 unused local variables ([a94494e](a94494e)) * remove redundant System.Reflection qualifier ([5367eb2](5367eb2)) * replace Activator.CreateInstance with plain Entity in ProjectAttributes ([a87bb5b](a87bb5b)) * replace generic exceptions with Dataverse-style FaultException ([fb21ac0](fb21ac0)) * replace obsolete HasCount() with Count().IsEqualTo() ([d8c5857](d8c5857)) * resolve ambiguous XML doc comment reference ([2a6be1b](2a6be1b)) * **retrieve:** translate QueryByAttribute Attributes/Values and Orders in RetrieveFake ([1bffa18](1bffa18)) * skip ownerid default when proxy type has no ownerid attribute ([714dd89](714dd89)) * support reverse-direction (ManyToOne) nested deep insert ([00df6e8](00df6e8)) * use Count property instead of Count() method ([49c69cd](49c69cd)) * validate maxRetrieveCount argument in WithMaxRetrieveCount ([3c7797d](3c7797d)) * validate relationship metadata matches parent entity in DeepInsertProcessor ([2a6952a](2a6952a)) ### Features * add FetchXmlToQueryExpression organization request fake ([5885467](5885467)) * add QueryExpressionToFetchXml organization request fake ([c0366cb](c0366cb)) * add RelatedEntitiesQuery support to RetrieveFake ([868a555](868a555)) * add RetrieveAllEntities organization request fake ([83db189](83db189)) * add RetrieveAttributeFake implementation ([e64ac18](e64ac18)) * **query:** implement CompareColumns and MatchFirstRowUsingCrossApply ([b4f9bd1](b4f9bd1)) * **query:** support EXISTS-style semi-joins via JoinOperator.Any/NotAny/Exists ([63df498](63df498)) * **query:** support FilterExpression.AnyAllFilterLinkEntity and JoinOperator.All/NotAll ([87704f4](87704f4)) * route Create/Update/Delete/Retrieve/Associate/Disassociate through Execute pipeline ([374604b](374604b)) * set audit fields and RowVersion on Create and Update ([f1c7f92](f1c7f92)) * support deep insert (RelatedEntities) in CreateFake and UpsertFake ([103f0de](103f0de)) * use Options class for explicit configuration ([8482732](8482732)) ### Performance Improvements * cache EntityLogicalNameAttribute lookup in CreateQuery<T> ([863ba8d](863ba8d)) * cache MethodInfo lookups in ConditionParser as static readonly fields ([0f89d70](0f89d70)) * compile proxy converter delegates via expression trees ([1c38463](1c38463)) * use pattern matching in XrmOrderByAttributeComparer ([f035a4a](f035a4a))
PepperPix
pushed a commit
that referenced
this pull request
Jun 19, 2026
# [1.1.0-beta.1](v1.0.0...v1.1.0-beta.1) (2026-06-19) ### Bug Fixes * **bulk-delete:** copy RecurrencePattern and StartDateTime to asyncoperation ([3cffda7](3cffda7)) * **bulk-delete:** omit recurrencepattern and recurrencestarttime when not set ([0c0a2e8](0c0a2e8)) * **bulk-delete:** populate name, ownerid, and operationtype on asyncoperation entity ([2ac6f28](2ac6f28)) * deep-clone OptionSetValue instances inside OptionSetValueCollection ([dd72956](dd72956)) * enforce single-child and entity type validation for N:1 deep insert ([7ae25aa](7ae25aa)) * ensure CloneEntity returns base Entity runtime type ([f393387](f393387)) * fall back to EntityLogicalNameAttribute in CreateQuery<T> when type is not in resolver cache ([5ba7140](5ba7140)) * merge attributes on Update instead of replacing the entire entity ([753ecd6](753ecd6)) * **query:** clone outer entity per row in LeftOuter join ([501db49](501db49)) * **query:** fix 4 critical query engine bugs ([ef0e7e4](ef0e7e4)) * **query:** guard against null Attributes array in PatchDateFormat ([3021776](3021776)) * **query:** handle null otherEntity in JoinAttributes for left outer joins ([a7c67cb](a7c67cb)) * **query:** honor LinkEntity.Orders in RetrieveMultiple ordering ([#42](#42)) ([4b363b1](4b363b1)) * **query:** honor root-level OrderExpression.EntityName for linked-entity sorting ([#43](#43)) ([102de5d](102de5d)) * **query:** match primary id attribute against Entity.Id in RetrieveMultiple ([5853fb6](5853fb6)) * **query:** materialize DateTime attributes before modifying collection in PatchDateFormat ([dd4d2e3](dd4d2e3)) * **query:** pass Guid directly for EqualUserId/EqualBusinessId conditions ([e7b8c77](e7b8c77)) * **query:** treat fetchxml bool condition values without metadata as bool ([8be757e](8be757e)) * **query:** use fiscalYear from condition as year for InFiscalYear date range ([e4677e6](e4677e6)) * **query:** use TimeOnly.MaxValue for end-of-day upper bound in date range operators ([49b1679](49b1679)) * **query:** validate actual values in aggregate FetchXml guards ([fe9cfcf](fe9cfcf)) * remove 14 redundant using directives ([1771050](1771050)) * remove 5 unused local variables ([a94494e](a94494e)) * remove redundant System.Reflection qualifier ([5367eb2](5367eb2)) * replace Activator.CreateInstance with plain Entity in ProjectAttributes ([a87bb5b](a87bb5b)) * replace generic exceptions with Dataverse-style FaultException ([fb21ac0](fb21ac0)) * replace obsolete HasCount() with Count().IsEqualTo() ([d8c5857](d8c5857)) * resolve ambiguous XML doc comment reference ([2a6be1b](2a6be1b)) * **retrieve:** translate QueryByAttribute Attributes/Values and Orders in RetrieveFake ([1bffa18](1bffa18)) * skip ownerid default when proxy type has no ownerid attribute ([714dd89](714dd89)) * support reverse-direction (ManyToOne) nested deep insert ([00df6e8](00df6e8)) * use Count property instead of Count() method ([49c69cd](49c69cd)) * validate maxRetrieveCount argument in WithMaxRetrieveCount ([3c7797d](3c7797d)) * validate relationship metadata matches parent entity in DeepInsertProcessor ([2a6952a](2a6952a)) ### Features * add FetchXmlToQueryExpression organization request fake ([5885467](5885467)) * add QueryExpressionToFetchXml organization request fake ([c0366cb](c0366cb)) * add RelatedEntitiesQuery support to RetrieveFake ([868a555](868a555)) * add RetrieveAllEntities organization request fake ([83db189](83db189)) * add RetrieveAttributeFake implementation ([e64ac18](e64ac18)) * **query:** implement CompareColumns and MatchFirstRowUsingCrossApply ([b4f9bd1](b4f9bd1)) * **query:** support EXISTS-style semi-joins via JoinOperator.Any/NotAny/Exists ([63df498](63df498)) * **query:** support FilterExpression.AnyAllFilterLinkEntity and JoinOperator.All/NotAll ([87704f4](87704f4)) * route Create/Update/Delete/Retrieve/Associate/Disassociate through Execute pipeline ([374604b](374604b)) * set audit fields and RowVersion on Create and Update ([f1c7f92](f1c7f92)) * support deep insert (RelatedEntities) in CreateFake and UpsertFake ([103f0de](103f0de)) * use Options class for explicit configuration ([8482732](8482732)) ### Performance Improvements * cache EntityLogicalNameAttribute lookup in CreateQuery<T> ([863ba8d](863ba8d)) * cache MethodInfo lookups in ConditionParser as static readonly fields ([0f89d70](0f89d70)) * compile proxy converter delegates via expression trees ([1c38463](1c38463)) * use pattern matching in XrmOrderByAttributeComparer ([f035a4a](f035a4a))
PepperPix
pushed a commit
that referenced
this pull request
Jun 19, 2026
# [1.1.0-beta.1](v1.0.0...v1.1.0-beta.1) (2026-06-19) ### Bug Fixes * **bulk-delete:** copy RecurrencePattern and StartDateTime to asyncoperation ([3cffda7](3cffda7)) * **bulk-delete:** omit recurrencepattern and recurrencestarttime when not set ([0c0a2e8](0c0a2e8)) * **bulk-delete:** populate name, ownerid, and operationtype on asyncoperation entity ([2ac6f28](2ac6f28)) * deep-clone OptionSetValue instances inside OptionSetValueCollection ([dd72956](dd72956)) * enforce single-child and entity type validation for N:1 deep insert ([7ae25aa](7ae25aa)) * ensure CloneEntity returns base Entity runtime type ([f393387](f393387)) * fall back to EntityLogicalNameAttribute in CreateQuery<T> when type is not in resolver cache ([5ba7140](5ba7140)) * merge attributes on Update instead of replacing the entire entity ([753ecd6](753ecd6)) * **query:** clone outer entity per row in LeftOuter join ([501db49](501db49)) * **query:** fix 4 critical query engine bugs ([ef0e7e4](ef0e7e4)) * **query:** guard against null Attributes array in PatchDateFormat ([3021776](3021776)) * **query:** handle null otherEntity in JoinAttributes for left outer joins ([a7c67cb](a7c67cb)) * **query:** honor LinkEntity.Orders in RetrieveMultiple ordering ([#42](#42)) ([4b363b1](4b363b1)) * **query:** honor root-level OrderExpression.EntityName for linked-entity sorting ([#43](#43)) ([102de5d](102de5d)) * **query:** match primary id attribute against Entity.Id in RetrieveMultiple ([5853fb6](5853fb6)) * **query:** materialize DateTime attributes before modifying collection in PatchDateFormat ([dd4d2e3](dd4d2e3)) * **query:** pass Guid directly for EqualUserId/EqualBusinessId conditions ([e7b8c77](e7b8c77)) * **query:** treat fetchxml bool condition values without metadata as bool ([8be757e](8be757e)) * **query:** use fiscalYear from condition as year for InFiscalYear date range ([e4677e6](e4677e6)) * **query:** use TimeOnly.MaxValue for end-of-day upper bound in date range operators ([49b1679](49b1679)) * **query:** validate actual values in aggregate FetchXml guards ([fe9cfcf](fe9cfcf)) * remove 14 redundant using directives ([1771050](1771050)) * remove 5 unused local variables ([a94494e](a94494e)) * remove redundant System.Reflection qualifier ([5367eb2](5367eb2)) * replace Activator.CreateInstance with plain Entity in ProjectAttributes ([a87bb5b](a87bb5b)) * replace generic exceptions with Dataverse-style FaultException ([fb21ac0](fb21ac0)) * replace obsolete HasCount() with Count().IsEqualTo() ([d8c5857](d8c5857)) * resolve ambiguous XML doc comment reference ([2a6be1b](2a6be1b)) * **retrieve:** translate QueryByAttribute Attributes/Values and Orders in RetrieveFake ([1bffa18](1bffa18)) * skip ownerid default when proxy type has no ownerid attribute ([714dd89](714dd89)) * support reverse-direction (ManyToOne) nested deep insert ([00df6e8](00df6e8)) * use Count property instead of Count() method ([49c69cd](49c69cd)) * validate maxRetrieveCount argument in WithMaxRetrieveCount ([3c7797d](3c7797d)) * validate relationship metadata matches parent entity in DeepInsertProcessor ([2a6952a](2a6952a)) ### Features * add FetchXmlToQueryExpression organization request fake ([5885467](5885467)) * add QueryExpressionToFetchXml organization request fake ([c0366cb](c0366cb)) * add RelatedEntitiesQuery support to RetrieveFake ([868a555](868a555)) * add RetrieveAllEntities organization request fake ([83db189](83db189)) * add RetrieveAttributeFake implementation ([e64ac18](e64ac18)) * **query:** implement CompareColumns and MatchFirstRowUsingCrossApply ([b4f9bd1](b4f9bd1)) * **query:** support EXISTS-style semi-joins via JoinOperator.Any/NotAny/Exists ([63df498](63df498)) * **query:** support FilterExpression.AnyAllFilterLinkEntity and JoinOperator.All/NotAll ([87704f4](87704f4)) * route Create/Update/Delete/Retrieve/Associate/Disassociate through Execute pipeline ([374604b](374604b)) * set audit fields and RowVersion on Create and Update ([f1c7f92](f1c7f92)) * support deep insert (RelatedEntities) in CreateFake and UpsertFake ([103f0de](103f0de)) * use Options class for explicit configuration ([8482732](8482732)) ### Performance Improvements * cache EntityLogicalNameAttribute lookup in CreateQuery<T> ([863ba8d](863ba8d)) * cache MethodInfo lookups in ConditionParser as static readonly fields ([0f89d70](0f89d70)) * compile proxy converter delegates via expression trees ([1c38463](1c38463)) * use pattern matching in XrmOrderByAttributeComparer ([f035a4a](f035a4a))
PepperPix
pushed a commit
that referenced
this pull request
Jun 19, 2026
# [1.1.0-beta.11](v1.1.0-beta.10...v1.1.0-beta.11) (2026-06-19) ### Bug Fixes * **query:** honor root-level OrderExpression.EntityName for linked-entity sorting ([#43](#43)) ([102de5d](102de5d))
|
🎉 This PR is included in version 1.1.0-beta.11 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
PepperPix
pushed a commit
that referenced
this pull request
Jul 1, 2026
# [1.1.0](v1.0.0...v1.1.0) (2026-07-01) ### Bug Fixes * **bulk-delete:** copy RecurrencePattern and StartDateTime to asyncoperation ([3cffda7](3cffda7)) * **bulk-delete:** omit recurrencepattern and recurrencestarttime when not set ([0c0a2e8](0c0a2e8)) * **bulk-delete:** populate name, ownerid, and operationtype on asyncoperation entity ([2ac6f28](2ac6f28)) * deep-clone OptionSetValue instances inside OptionSetValueCollection ([dd72956](dd72956)) * enforce single-child and entity type validation for N:1 deep insert ([7ae25aa](7ae25aa)) * ensure CloneEntity returns base Entity runtime type ([f393387](f393387)) * fall back to EntityLogicalNameAttribute in CreateQuery<T> when type is not in resolver cache ([5ba7140](5ba7140)) * merge attributes on Update instead of replacing the entire entity ([753ecd6](753ecd6)) * **query:** clone outer entity per row in LeftOuter join ([501db49](501db49)) * **query:** fix 4 critical query engine bugs ([ef0e7e4](ef0e7e4)) * **query:** guard against null Attributes array in PatchDateFormat ([3021776](3021776)) * **query:** handle null otherEntity in JoinAttributes for left outer joins ([a7c67cb](a7c67cb)) * **query:** honor LinkEntity.Orders in RetrieveMultiple ordering ([#42](#42)) ([4b363b1](4b363b1)) * **query:** honor root-level OrderExpression.EntityName for linked-entity sorting ([#43](#43)) ([102de5d](102de5d)) * **query:** match primary id attribute against Entity.Id in RetrieveMultiple ([5853fb6](5853fb6)) * **query:** materialize DateTime attributes before modifying collection in PatchDateFormat ([dd4d2e3](dd4d2e3)) * **query:** pass Guid directly for EqualUserId/EqualBusinessId conditions ([e7b8c77](e7b8c77)) * **query:** prefer OrderExpression.Alias over EntityName in CollectOrders ([#44](#44)) ([890d912](890d912)) * **query:** treat fetchxml bool condition values without metadata as bool ([8be757e](8be757e)) * **query:** use fiscalYear from condition as year for InFiscalYear date range ([e4677e6](e4677e6)) * **query:** use TimeOnly.MaxValue for end-of-day upper bound in date range operators ([49b1679](49b1679)) * **query:** validate actual values in aggregate FetchXml guards ([fe9cfcf](fe9cfcf)) * remove 14 redundant using directives ([1771050](1771050)) * remove 5 unused local variables ([a94494e](a94494e)) * remove redundant System.Reflection qualifier ([5367eb2](5367eb2)) * replace Activator.CreateInstance with plain Entity in ProjectAttributes ([a87bb5b](a87bb5b)) * replace generic exceptions with Dataverse-style FaultException ([fb21ac0](fb21ac0)) * replace obsolete HasCount() with Count().IsEqualTo() ([d8c5857](d8c5857)) * resolve ambiguous XML doc comment reference ([2a6be1b](2a6be1b)) * **retrieve:** translate QueryByAttribute Attributes/Values and Orders in RetrieveFake ([1bffa18](1bffa18)) * skip ownerid default when proxy type has no ownerid attribute ([714dd89](714dd89)) * support reverse-direction (ManyToOne) nested deep insert ([00df6e8](00df6e8)) * use Count property instead of Count() method ([49c69cd](49c69cd)) * validate maxRetrieveCount argument in WithMaxRetrieveCount ([3c7797d](3c7797d)) * validate relationship metadata matches parent entity in DeepInsertProcessor ([2a6952a](2a6952a)) ### Features * add FetchXmlToQueryExpression organization request fake ([5885467](5885467)) * add QueryExpressionToFetchXml organization request fake ([c0366cb](c0366cb)) * add RelatedEntitiesQuery support to RetrieveFake ([868a555](868a555)) * add RetrieveAllEntities organization request fake ([83db189](83db189)) * add RetrieveAttributeFake implementation ([e64ac18](e64ac18)) * **query:** implement CompareColumns and MatchFirstRowUsingCrossApply ([b4f9bd1](b4f9bd1)) * **query:** support EXISTS-style semi-joins via JoinOperator.Any/NotAny/Exists ([63df498](63df498)) * **query:** support FilterExpression.AnyAllFilterLinkEntity and JoinOperator.All/NotAll ([87704f4](87704f4)) * route Create/Update/Delete/Retrieve/Associate/Disassociate through Execute pipeline ([374604b](374604b)) * set audit fields and RowVersion on Create and Update ([f1c7f92](f1c7f92)) * support deep insert (RelatedEntities) in CreateFake and UpsertFake ([103f0de](103f0de)) * use Options class for explicit configuration ([8482732](8482732)) ### Performance Improvements * cache EntityLogicalNameAttribute lookup in CreateQuery<T> ([863ba8d](863ba8d)) * cache MethodInfo lookups in ConditionParser as static readonly fields ([0f89d70](0f89d70)) * compile proxy converter delegates via expression trees ([1c38463](1c38463)) * use pattern matching in XrmOrderByAttributeComparer ([f035a4a](f035a4a))
|
🎉 This PR is included in version 1.1.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
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.
Root-level OrderExpression entries with a non-null EntityName (produced by FetchXML
<order entityname="alias" attribute="..." />or set directly on qe.Orders) were silently ignored. CollectOrders passed alias=null for all root orders, so the key resolver looked up the bare attribute name instead of "{alias}.{attribute}", returning rows in an unordered sequence and causing wrong results when combined with TopCount.entitynameXML attribute and populate OrderExpression.EntityName