Conversation
…bool
FetchXml conditions on attributes whose type cannot be resolved via
early-bound model assemblies leaked through GetConditionExpressionValueCast
as strings ("true"/"false"). The left-hand side was cast to bool while
the right-hand side stayed a string, so equality always evaluated to false.
Add a bool.TryParse fallback alongside the existing Guid/numeric/DateTime
fallbacks so bool conditions match correctly without registered metadata.
Co-authored-by: Junie <junie@jetbrains.com>
Co-authored-by: Junie <junie@jetbrains.com>
Co-authored-by: Junie <junie@jetbrains.com>
Co-authored-by: Junie <junie@jetbrains.com>
Co-authored-by: Junie <junie@jetbrains.com>
Co-authored-by: Junie <junie@jetbrains.com>
following qodana
# [1.1.0-beta.1](v1.0.0...v1.1.0-beta.1) (2026-05-22) ### Bug Fixes * **query:** treat fetchxml bool condition values without metadata as bool ([fe18d0c](fe18d0c)) ### Features * add FetchXmlToQueryExpression organization request fake ([de7cd6f](de7cd6f)) * add QueryExpressionToFetchXml organization request fake ([a18c9b3](a18c9b3)) * add RetrieveAllEntities organization request fake ([f826094](f826094))
…ultiple RetrieveMultiple did not find entities when filtering by the primary key attribute (e.g. systemuserid == <guid>), because the filter pipeline only inspects the Attributes collection while stored entities typically have the primary id only in Entity.Id. CreateQuery<T> now ensures the primary id attribute is present on cloned entities, using EntityMetadata.PrimaryIdAttribute when available or the <entity>id convention as fallback. Co-authored-by: Junie <junie@jetbrains.com>
# [1.1.0-beta.2](v1.1.0-beta.1...v1.1.0-beta.2) (2026-05-22) ### Bug Fixes * **query:** match primary id attribute against Entity.Id in RetrieveMultiple ([a66b55f](a66b55f))
…on in PatchDateFormat The LINQ .Where() enumeration over record.Attributes was deferred, causing a 'Collection was modified' exception when the loop body modified the same Attributes dictionary. Adding .ToList() materializes the filtered results before iteration begins. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When EntityMetadata is registered with only a LogicalName but no AttributeMetadata array, the Attributes property is null. Accessing .SingleOrDefault() on null throws ArgumentNullException. Added an early return when Attributes is null. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…joins When a left outer join produces no matching record, DefaultIfEmpty() yields null. JoinAttributes now returns the original entity unchanged when otherEntity is null, matching Dataverse behavior where unmatched outer joins simply omit the linked attributes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover three scenarios that lock in expected LeftOuter join semantics: - Outer entity is returned even when no matching inner entity exists - Multiple matching inner entities produce multiple result rows - LeftOuter returns more rows than the equivalent Inner join Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# [1.1.0-beta.3](v1.1.0-beta.2...v1.1.0-beta.3) (2026-05-22) ### Bug Fixes * **query:** guard against null Attributes array in PatchDateFormat ([fdefc11](fdefc11)) * **query:** handle null otherEntity in JoinAttributes for left outer joins ([1a58675](1a58675)) * **query:** materialize DateTime attributes before modifying collection in PatchDateFormat ([9b9aa2d](9b9aa2d))
Replace FastCloner.DeepClone with manual construction of a plain Entity instance so that the runtime type is always Entity (true upcast). Attributes, FormattedValues, KeyAttributes and RelatedEntities are deep-copied via the existing CloneAttribute helper. Add ToProxyType conversion in RetrieveMultipleFake so that typed LINQ queries (DataContext) still receive the correct proxy type at query time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ributes The Activator.CreateInstance(entity.GetType()) call is unnecessary now that the internal store only holds base Entity instances. Use a simple new Entity(...) constructor consistent with the ColumnSet overload. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move proxy type conversion from per-call reflection (GetMethod + MakeGenericMethod + Invoke) to pre-built cached delegates in EntityTypeResolver.ConvertToProxyType. This eliminates repeated reflection overhead in RetrieveMultipleFake. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace per-call GetCustomAttribute reflection with a static dictionary cache keyed by Type. The attribute is resolved once per entity type and reused for all subsequent CreateQuery<T>() calls. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace GetType() + typeof chain with switch pattern matching. Eliminates per-comparison GetType() call and sequential type equality checks, letting the JIT use a jump table instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove static EntityLogicalNameCache to avoid shared state across tests. Instead, use EntityTypeResolver.GetLogicalName() backed by an instance- scoped reverse type cache (Type → logicalName). All caches are now owned by the FakeOrganizationService instance — no cross-test side effects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…fields Replace ~20 per-query GetMethod reflection calls with static readonly MethodInfo fields resolved once at class initialization. Covers string operations, DateTime.Date, HashSet methods, SDK property getters (EntityReference.Id/Name, Money.Value, OptionSetValue.Value, etc.), and the dynamic Like operator (Contains/StartsWith/EndsWith). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In the LeftOuter join path GroupJoin + SelectMany called x.outerEl.JoinAttributes(...) without cloning first. When an outer entity matched multiple inner entities every SelectMany iteration mutated the same instance, so all result rows ended up sharing the last inner entity's aliased values. Add CloneEntity() before JoinAttributes() to mirror the Inner-join case and ensure each result row is independent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…alue assertions Assert that the two corpB result rows are distinct object instances (not the same mutated reference) and that each row carries the aliased first name of its specific contact (John B / John C). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
BuildProxyConverterCache used a lambda that closed over a MethodInfo and called Invoke on every conversion, keeping full reflection overhead despite the caching. Replace with Expression.Lambda<Func<Entity,Entity>>.Compile() so each cached delegate is emitted as real IL and ConvertToProxyType is truly reflection-free per call. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CloneAttribute for OptionSetValueCollection used ToArray() which shared the original OptionSetValue references. Since OptionSetValue.Value has a setter, mutations to a cloned entity could silently affect the original. Replace ToArray() with a Select(v => new OptionSetValue(v.Value)) so each element is its own copy. Also rename CloneEntity_Should_ReturnEntityRuntimeType_WhenSourceIsDerived to CloneEntity_Should_ReturnBaseEntityType_EvenWhenSourceIsDerived — the old name implied the derived type is preserved, but the intended (and asserted) behaviour is the opposite. Add CloneEntity_OptionSetValueCollection_Should_BeIndependent to lock in clone immutability for OptionSetValueCollection attributes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ype is not in resolver cache TypeResolver.GetLogicalName only finds types discovered via ProxyTypesAssembly scanning. If the assembly was not registered, GetLogicalName returned null and the exception claimed the attribute was missing, which was misleading. Now fall back to reading EntityLogicalNameAttribute directly from typeof(T) before throwing, so CreateQuery<T> works for any properly annotated proxy type regardless of assembly registration. The error message now also explains the two registration paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace explicit int type with var in for loops - Improves code readability Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Specify AddMetadata(EntityMetadata) overload in documentation - Fixes InvalidXmlDocComment warning Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Columns Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The SourceGenerator requires [FastClonerClonable] on types to generate FastDeepClone() extension methods at compile time. The only clone use case in this project is QueryExpression (external Dataverse SDK type), which cannot be annotated. The package was therefore never active. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rtProcessor ProcessOneToMany now checks that metadata.ReferencedEntity matches the parentLogicalName. ProcessManyToMany validates that the parent is either Entity1 or Entity2 of the relationship. Both throw an InvalidArgument fault on mismatch instead of silently setting incorrect foreign keys. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… not set Only persist recurrencepattern when non-null/empty and recurrencestarttime when non-default, mirroring Dataverse behavior where unset attributes are absent rather than holding sentinel values. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Columns test The test previously asserted ElapsedMilliseconds > 0 which is unreliable on fast machines. Now asserts correct query results across 100 repeated CompareColumns queries, verifying functional correctness rather than wall-clock time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# [1.1.0-beta.7](v1.1.0-beta.6...v1.1.0-beta.7) (2026-05-26) ### Bug Fixes * **bulk-delete:** copy RecurrencePattern and StartDateTime to asyncoperation ([97d08d0](97d08d0)) * **bulk-delete:** omit recurrencepattern and recurrencestarttime when not set ([665a695](665a695)) * **query:** fix 4 critical query engine bugs ([9bcbdc2](9bcbdc2)) * remove 14 redundant using directives ([66f139b](66f139b)) * remove 5 unused local variables ([9ef79de](9ef79de)) * remove redundant System.Reflection qualifier ([c11bd75](c11bd75)) * replace obsolete HasCount() with Count().IsEqualTo() ([24d9719](24d9719)) * resolve ambiguous XML doc comment reference ([f3a3928](f3a3928)) * use Count property instead of Count() method ([1577b52](1577b52)) * validate relationship metadata matches parent entity in DeepInsertProcessor ([fdafece](fdafece)) ### Features * **query:** implement CompareColumns and MatchFirstRowUsingCrossApply ([7376e02](7376e02)) * **query:** support FilterExpression.AnyAllFilterLinkEntity and JoinOperator.All/NotAll ([628fd01](628fd01)) * support deep insert (RelatedEntities) in CreateFake and UpsertFake ([07ef938](07ef938))
ProcessOneToMany now handles both directions: - Parent is Referenced side → sets FK on children (existing behavior) - Parent is Referencing side → creates child first, then sets FK on parent This fixes nested deep inserts like Calendar → CalendarRule → InnerCalendar where the inner relationship goes from the Referencing to the Referenced side. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A lookup attribute can only reference one record. The N:1 (reverse) branch now throws an InvalidArgument fault when multiple children are provided, preventing silent data loss where only the last child would be linked. Also validates that the child's logical name matches metadata.ReferencedEntity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# [1.1.0-beta.8](v1.1.0-beta.7...v1.1.0-beta.8) (2026-05-26) ### Bug Fixes * enforce single-child and entity type validation for N:1 deep insert ([4242368](4242368)) * support reverse-direction (ManyToOne) nested deep insert ([d05ff50](d05ff50))
There was a problem hiding this comment.
Pull request overview
This PR prepares the next beta release of Digitall.Dataverse.Testing by replacing environment-variable defaults with per-service options, broadening fake Dataverse request/query support, and adding extensive TUnit coverage for the new behavior.
Changes:
- Introduces
FakeDataverseOptionsand builder helpers for current user, business unit, fiscal year start, and retrieve limits. - Expands request fakes and query processing, including deep insert, metadata retrieval, FetchXml/QueryExpression conversion, advanced joins, aggregate fixes, and audit/default field behavior.
- Updates documentation, package references, analyzer scope, and test guidance for the release.
Reviewed changes
Copilot reviewed 84 out of 86 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
.gitignore |
Adds local tool/output ignores. |
AGENTS.md |
Documents TUnit --treenode-filter usage. |
Directory.Build.props |
Bumps package/release version metadata. |
README.md |
Documents new options, request fakes, joins, and deep insert. |
qodana.yaml |
Adjusts Qodana exclusions. |
src/Digitall.Dataverse.Testing/Digitall.Dataverse.Testing.csproj |
Removes source generator package. |
src/Digitall.Dataverse.Testing/EntityTypeResolver.cs |
Adds metadata-only entity support and proxy conversion cache. |
src/Digitall.Dataverse.Testing/Errors/ErrorCodes.cs |
Adds MessageDoesNotExist. |
src/Digitall.Dataverse.Testing/Extensions/DateTimeExtensions.cs |
Reworks week boundary helpers. |
src/Digitall.Dataverse.Testing/Extensions/EntityExtensions.cs |
Updates cloning/projection and aliased join handling. |
src/Digitall.Dataverse.Testing/Extensions/FakeDataverseBuilderExtensions.cs |
Adds option-setting builder methods. |
src/Digitall.Dataverse.Testing/FakeDataverseOptions.cs |
Adds per-service configuration options. |
src/Digitall.Dataverse.Testing/Logic/Queries/ExpressionProcessor.cs |
Adds any/all filter support and idempotent alias handling. |
src/Digitall.Dataverse.Testing/Logic/Queries/FetchProcessor.cs |
Adds FetchXml operator/link handling and bool fallback parsing. |
src/Digitall.Dataverse.Testing/Logic/Queries/LinkedEntitiesProcessor.cs |
Adds advanced join operators. |
src/Digitall.Dataverse.Testing/Logic/Queries/QueryExpressionExtensions.cs |
Supports recursive alias lookup. |
src/Digitall.Dataverse.Testing/Logic/Queries/QueryProcessor.cs |
Updates aggregate validation/error handling and ordering. |
src/Digitall.Dataverse.Testing/Logic/Queries/Validators.cs |
Allows null filter validation input. |
src/Digitall.Dataverse.Testing/Logic/XrmOrderByAttributeComparer.cs |
Refactors ordering comparisons and aliased values. |
src/Digitall.Dataverse.Testing/OrganizationRequests/AssociateFake.cs |
Implements relationship-aware associate behavior. |
src/Digitall.Dataverse.Testing/OrganizationRequests/BulkDeleteFake.cs |
Populates async operation metadata. |
src/Digitall.Dataverse.Testing/OrganizationRequests/CreateFake.cs |
Routes create through core logic and deep insert. |
src/Digitall.Dataverse.Testing/OrganizationRequests/DeepInsertProcessor.cs |
Adds deep insert helper. |
src/Digitall.Dataverse.Testing/OrganizationRequests/DeleteFake.cs |
Uses delete core logic. |
src/Digitall.Dataverse.Testing/OrganizationRequests/DisassociateFake.cs |
Implements M:N disassociate behavior. |
src/Digitall.Dataverse.Testing/OrganizationRequests/FetchXmlToQueryExpressionFake.cs |
Adds FetchXml-to-query request fake. |
src/Digitall.Dataverse.Testing/OrganizationRequests/QueryExpressionToFetchXmlFake.cs |
Adds query-to-FetchXml request fake. |
src/Digitall.Dataverse.Testing/OrganizationRequests/RetrieveAllEntitiesFake.cs |
Adds all-metadata retrieval fake. |
src/Digitall.Dataverse.Testing/OrganizationRequests/RetrieveFake.cs |
Adds related entity retrieval support. |
src/Digitall.Dataverse.Testing/OrganizationRequests/RetrieveMultipleFake.cs |
Uses options for paging and improves faults/projection. |
src/Digitall.Dataverse.Testing/OrganizationRequests/UpdateFake.cs |
Uses update core logic. |
src/Digitall.Dataverse.Testing/OrganizationRequests/UpsertFake.cs |
Adds deep insert handling on upsert paths. |
src/Digitall.Dataverse.Testing/OrganizationRequests/WhoAmIFake.cs |
Uses service options for identity data. |
src/Digitall.Dataverse.Testing/PluginExecutionContextBuilder.cs |
Uses fake service options for default user ID. |
src/Digitall.Dataverse.Testing/packages.lock.json |
Updates source dependency lock state. |
tests/Digitall.Dataverse.Testing.Tests/.env |
Removes environment-based test configuration content. |
tests/Digitall.Dataverse.Testing.Tests/Digitall.Dataverse.Testing.Tests.csproj |
Updates test dependencies and removes DotNetEnv. |
tests/Digitall.Dataverse.Testing.Tests/Errors/ErrorFactoryTests.cs |
Adds error factory tests. |
tests/Digitall.Dataverse.Testing.Tests/Extensions/BuilderExtensionsTests.cs |
Adds max retrieve count builder tests. |
tests/Digitall.Dataverse.Testing.Tests/Extensions/DateTimeExtensionsTests.cs |
Updates date extension coverage. |
tests/Digitall.Dataverse.Testing.Tests/Extensions/EntityExtensionsTests.cs |
Adds clone independence coverage. |
tests/Digitall.Dataverse.Testing.Tests/Extensions/PluginExecutionContextBuilderExtensionsTests.cs |
Removes env precedence tests. |
tests/Digitall.Dataverse.Testing.Tests/FakeDataverseBuilderTests.cs |
Adds option builder tests. |
tests/Digitall.Dataverse.Testing.Tests/FakeOrganizationServiceTests.cs |
Adds metadata, audit, owner, and update merge tests. |
tests/Digitall.Dataverse.Testing.Tests/Logic/AggregateFetchXmlBehaviorTests.cs |
Adds aggregate behavior coverage. |
tests/Digitall.Dataverse.Testing.Tests/Logic/AggregateFetchXmlValidationTests.cs |
Adds aggregate validation coverage. |
tests/Digitall.Dataverse.Testing.Tests/Logic/CompareColumnsTests.cs |
Adds compare-columns query tests. |
tests/Digitall.Dataverse.Testing.Tests/Logic/CrossApplyJoinTests.cs |
Adds cross-apply join tests. |
tests/Digitall.Dataverse.Testing.Tests/Logic/ExpressionProcessorTests.cs |
Adds filter and alias reuse tests. |
tests/Digitall.Dataverse.Testing.Tests/Logic/FetchProcessorTests.cs |
Adds FetchXml conversion/query tests. |
tests/Digitall.Dataverse.Testing.Tests/Logic/LinkEntityColumnProjectionTests.cs |
Adds linked column projection tests. |
tests/Digitall.Dataverse.Testing.Tests/Logic/NaturalJoinTests.cs |
Adds natural join tests. |
tests/Digitall.Dataverse.Testing.Tests/Logic/QueryBugsTests.cs |
Adds regression tests for query fixes. |
tests/Digitall.Dataverse.Testing.Tests/Logic/QueryProcessorTests.cs |
Adds query orchestration tests. |
tests/Digitall.Dataverse.Testing.Tests/Logic/QueryTests.cs |
Replaces env setup with options and adds fiscal/user tests. |
tests/Digitall.Dataverse.Testing.Tests/Logic/ValidatorsTests.cs |
Adds alias validator tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/AssignRequestFakeTests.cs |
Adds assign fake tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/AssociateFakeTests.cs |
Adds associate fake tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/BulkDeleteFakeTests.cs |
Adds bulk delete fake tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/DeleteFakeTests.cs |
Adds delete fake tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/DisassociateFakeTests.cs |
Adds disassociate fake tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/ExecuteTransactionFakeTests.cs |
Adds transaction fake tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/FetchXmlToQueryExpressionTests.cs |
Adds FetchXml conversion request tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/OrganizationRequestFakeTests.cs |
Adds assign preservation coverage. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/QueryExpressionToFetchXmlTests.cs |
Adds query conversion request tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/RetrieveAllEntitiesTests.cs |
Adds all metadata retrieval tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/RetrieveEntityFakeTests.cs |
Adds entity metadata retrieval tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/RetrieveMultipleFetchXmlBoolTests.cs |
Adds bool FetchXml regression tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/SetStateFakeTests.cs |
Adds state/status fake tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/UpdateFakeTests.cs |
Adds update fake tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/UpsertFakeDeepInsertTests.cs |
Adds upsert deep insert tests. |
tests/Digitall.Dataverse.Testing.Tests/OrganizationRequests/WhoAmITests.cs |
Removes DotNetEnv setup. |
tests/Digitall.Dataverse.Testing.Tests/PluginExecutionContextBuilderTests.cs |
Updates user ID default tests and formatting. |
tests/Digitall.Dataverse.Testing.Tests/packages.lock.json |
Updates test dependency lock state. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| writer.WriteAttributeString("alias", link.EntityAlias); | ||
| } | ||
|
|
||
| writer.WriteAttributeString("link-type", link.JoinOperator == JoinOperator.LeftOuter ? "outer" : "inner"); |
| if (values.Count == 1) | ||
| { |
| { | ||
| throw new Exception("Missing name for attribute in aggregate fetch xml"); | ||
| } | ||
| // logicalName is guaranteed non-empty by ValidateXmlDocument (requires "name" on <attribute>) |
| { | ||
| throw new Exception("An alias is required for an order clause for an aggregate Query."); | ||
| } | ||
| // alias is guaranteed present by ValidateXmlDocument (requires "alias" on <order> in aggregate) |
| var parentRef = new EntityReference(parentLogicalName, parentId); | ||
|
|
||
| foreach (var child in children.Entities) | ||
| { |
| var isFrom1To2 = parentLogicalName == metadata.Entity1LogicalName; | ||
| var fromAttribute = isFrom1To2 ? metadata.Entity1IntersectAttribute : metadata.Entity2IntersectAttribute; | ||
| var toAttribute = isFrom1To2 ? metadata.Entity2IntersectAttribute : metadata.Entity1IntersectAttribute; | ||
|
|
||
| foreach (var child in children.Entities) | ||
| { |
| if (user != null) | ||
| { | ||
| var buId = GetBusinessUnitId(user); | ||
| var buId = GetBusinessUnitId(user) ?? state.Options.BusinessUnitId; | ||
| results.Add("BusinessUnitId", buId); |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# [1.1.0-beta.9](v1.1.0-beta.8...v1.1.0-beta.9) (2026-06-18) ### Features * add RetrieveAttributeFake implementation ([f2474ff](f2474ff))
|
🎉 This PR is included in version 1.1.0-beta.9 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
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
|
* fix(query): honor LinkEntity.Orders in RetrieveMultiple ordering
The in-memory query engine only applied the root QueryExpression.Orders and
silently dropped orders defined on link entities. Combined with TopCount this
returned the wrong rows when sorting by a linked-entity attribute.
OrderQuery now collects orders from the root entity and every link entity
(recursively, including nested links) into a single OrderBy -> ThenBy chain,
applying root orders first followed by link orders in depth-first traversal
order. The key resolver reads aliased linked attributes ("{alias}.{attribute}")
and unwraps AliasedValue, reusing XrmOrderByAttributeComparer. Ordering still
runs before TopCount/paging.
FetchProcessor now parses <order> inside <link-entity> into LinkEntity.Orders
for FetchXml parity.
* chore: address qodana issues
* chore: more qodana fixes
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
# [1.1.0-beta.10](v1.1.0-beta.9...v1.1.0-beta.10) (2026-06-18) ### Bug Fixes * **query:** honor LinkEntity.Orders in RetrieveMultiple ordering ([#42](#42)) ([809f8e6](809f8e6))
|
🎉 This PR is included in version 1.1.0-beta.10 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 1.1.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
No description provided.