|
8 | 8 | using System;
|
9 | 9 | using System.Collections.Generic;
|
10 | 10 | using System.ComponentModel;
|
| 11 | +using System.Diagnostics; |
11 | 12 | using System.Diagnostics.Contracts;
|
12 | 13 | using System.Linq;
|
13 | 14 | using Microsoft.AspNetCore.OData.Edm;
|
@@ -69,7 +70,16 @@ public static Uri GenerateNavigationPropertyLink(this ResourceContext resourceCo
|
69 | 70 | throw Error.ArgumentNull(nameof(resourceContext));
|
70 | 71 | }
|
71 | 72 |
|
72 |
| - IList<ODataPathSegment> navigationPathSegments = resourceContext.GenerateBaseODataPathSegments(); |
| 73 | + IList<ODataPathSegment> navigationPathSegments; |
| 74 | + if (resourceContext.NavigationSource is IEdmContainedEntitySet && |
| 75 | + resourceContext.NavigationSource != resourceContext.SerializerContext.Path.NavigationSource()) |
| 76 | + { |
| 77 | + navigationPathSegments = resourceContext.GenerateContainmentODataPathSegments(); |
| 78 | + } |
| 79 | + else |
| 80 | + { |
| 81 | + navigationPathSegments = resourceContext.GenerateBaseODataPathSegments(); |
| 82 | + } |
73 | 83 |
|
74 | 84 | if (includeCast)
|
75 | 85 | {
|
@@ -429,8 +439,7 @@ private static void GenerateBaseODataPathSegments(
|
429 | 439 | // the case.
|
430 | 440 | odataPath.Clear();
|
431 | 441 |
|
432 |
| - IEdmContainedEntitySet containmnent = navigationSource as IEdmContainedEntitySet; |
433 |
| - if (containmnent != null) |
| 442 | + if (navigationSource is IEdmContainedEntitySet) |
434 | 443 | {
|
435 | 444 | EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
|
436 | 445 | IEdmEntitySet entitySet = new EdmEntitySet(container, navigationSource.Name,
|
@@ -465,5 +474,107 @@ private static void GenerateBaseODataPathSegmentsForFeed(
|
465 | 474 | feedContext.EntitySetBase,
|
466 | 475 | odataPath);
|
467 | 476 | }
|
| 477 | + |
| 478 | + private static IList<ODataPathSegment> GenerateContainmentODataPathSegments(this ResourceContext resourceContext) |
| 479 | + { |
| 480 | + List<ODataPathSegment> navigationPathSegments = new List<ODataPathSegment>(); |
| 481 | + ResourceContext currentResourceContext = resourceContext; |
| 482 | + |
| 483 | + // We loop till the base of the $expand expression then use GenerateBaseODataPathSegments to generate the base path segments |
| 484 | + // For instance, given $expand=Tabs($expand=Items($expand=Notes($expand=Tips))), we loop until we get to Tabs at the base |
| 485 | + while (currentResourceContext != null && currentResourceContext.NavigationSource != resourceContext.SerializerContext.Path.NavigationSource()) |
| 486 | + { |
| 487 | + if (currentResourceContext.NavigationSource is IEdmContainedEntitySet containedEntitySet) |
| 488 | + { |
| 489 | + // Type-cast segment for the expanded resource that is passed into the method is added by the caller |
| 490 | + if (currentResourceContext != resourceContext && currentResourceContext.StructuredType != containedEntitySet.EntityType()) |
| 491 | + { |
| 492 | + navigationPathSegments.Add(new TypeSegment(currentResourceContext.StructuredType, currentResourceContext.NavigationSource)); |
| 493 | + } |
| 494 | + |
| 495 | + KeySegment keySegment = new KeySegment( |
| 496 | + ConventionsHelpers.GetEntityKey(currentResourceContext), |
| 497 | + currentResourceContext.StructuredType as IEdmEntityType, |
| 498 | + navigationSource: currentResourceContext.NavigationSource); |
| 499 | + navigationPathSegments.Add(keySegment); |
| 500 | + |
| 501 | + NavigationPropertySegment navPropertySegment = new NavigationPropertySegment( |
| 502 | + containedEntitySet.NavigationProperty, |
| 503 | + containedEntitySet.ParentNavigationSource); |
| 504 | + navigationPathSegments.Add(navPropertySegment); |
| 505 | + } |
| 506 | + else if (currentResourceContext.NavigationSource is IEdmEntitySet entitySet) |
| 507 | + { |
| 508 | + // We will get here if there's a non-contained entity set on the $expand expression |
| 509 | + if (currentResourceContext.StructuredType != entitySet.EntityType()) |
| 510 | + { |
| 511 | + navigationPathSegments.Add(new TypeSegment(currentResourceContext.StructuredType, currentResourceContext.NavigationSource)); |
| 512 | + } |
| 513 | + |
| 514 | + KeySegment keySegment = new KeySegment( |
| 515 | + ConventionsHelpers.GetEntityKey(currentResourceContext), |
| 516 | + currentResourceContext.StructuredType as IEdmEntityType, |
| 517 | + currentResourceContext.NavigationSource); |
| 518 | + navigationPathSegments.Add(keySegment); |
| 519 | + |
| 520 | + EntitySetSegment entitySetSegment = new EntitySetSegment(entitySet); |
| 521 | + navigationPathSegments.Add(entitySetSegment); |
| 522 | + |
| 523 | + // Reverse the list such that the segments are in the right order |
| 524 | + navigationPathSegments.Reverse(); |
| 525 | + return navigationPathSegments; |
| 526 | + } |
| 527 | + else if (currentResourceContext.NavigationSource is IEdmSingleton singleton) |
| 528 | + { |
| 529 | + // We will get here if there's a singleton on the $expand expression |
| 530 | + if (currentResourceContext.StructuredType != singleton.EntityType()) |
| 531 | + { |
| 532 | + navigationPathSegments.Add(new TypeSegment(currentResourceContext.StructuredType, currentResourceContext.NavigationSource)); |
| 533 | + } |
| 534 | + |
| 535 | + SingletonSegment singletonSegment = new SingletonSegment(singleton); |
| 536 | + navigationPathSegments.Add(singletonSegment); |
| 537 | + |
| 538 | + // Reverse the list such that the segments are in the right order |
| 539 | + navigationPathSegments.Reverse(); |
| 540 | + return navigationPathSegments; |
| 541 | + } |
| 542 | + |
| 543 | + currentResourceContext = currentResourceContext.SerializerContext.ExpandedResource; |
| 544 | + } |
| 545 | + |
| 546 | + Debug.Assert(currentResourceContext != null, "currentResourceContext != null"); |
| 547 | + // Once we are at the base of the $expand expression, we call GenerateBaseODataPathSegments to generate the base path segments |
| 548 | + IList<ODataPathSegment> pathSegments = currentResourceContext.GenerateBaseODataPathSegments(); |
| 549 | + |
| 550 | + Debug.Assert(pathSegments.Count > 0, "pathSegments.Count > 0"); |
| 551 | + |
| 552 | + ODataPathSegment lastNonKeySegment; |
| 553 | + |
| 554 | + if (pathSegments.Count == 1) |
| 555 | + { |
| 556 | + lastNonKeySegment = pathSegments[0]; |
| 557 | + Debug.Assert(lastNonKeySegment is SingletonSegment, "lastNonKeySegment is SingletonSegment"); |
| 558 | + } |
| 559 | + else |
| 560 | + { |
| 561 | + Debug.Assert(pathSegments[pathSegments.Count - 1] is KeySegment, "pathSegments[pathSegments.Count - 1] is KeySegment"); |
| 562 | + // 2nd last segment would be NavigationPathSegment or EntitySetSegment |
| 563 | + lastNonKeySegment = pathSegments[pathSegments.Count - 2]; |
| 564 | + } |
| 565 | + |
| 566 | + if (currentResourceContext.StructuredType != lastNonKeySegment.EdmType.AsElementType()) |
| 567 | + { |
| 568 | + pathSegments.Add(new TypeSegment(currentResourceContext.StructuredType, currentResourceContext.NavigationSource)); |
| 569 | + } |
| 570 | + |
| 571 | + // Add the segments from the $expand expression in reverse order |
| 572 | + for (int i = navigationPathSegments.Count - 1; i >= 0; i--) |
| 573 | + { |
| 574 | + pathSegments.Add(navigationPathSegments[i]); |
| 575 | + } |
| 576 | + |
| 577 | + return pathSegments; |
| 578 | + } |
468 | 579 | }
|
469 | 580 | }
|
0 commit comments