|
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.AspNet.OData.Builder.Conventions; |
@@ -81,7 +82,16 @@ public static Uri GenerateNavigationPropertyLink(this ResourceContext resourceCo |
81 | 82 | throw Error.Argument("resourceContext", SRResources.UrlHelperNull, typeof(ResourceContext).Name); |
82 | 83 | } |
83 | 84 |
|
84 | | - IList<ODataPathSegment> navigationPathSegments = resourceContext.GenerateBaseODataPathSegments(); |
| 85 | + IList<ODataPathSegment> navigationPathSegments; |
| 86 | + if (resourceContext.NavigationSource is IEdmContainedEntitySet && |
| 87 | + resourceContext.NavigationSource != resourceContext.SerializerContext.Path.NavigationSource) |
| 88 | + { |
| 89 | + navigationPathSegments = resourceContext.GenerateContainmentODataPathSegments(); |
| 90 | + } |
| 91 | + else |
| 92 | + { |
| 93 | + navigationPathSegments = resourceContext.GenerateBaseODataPathSegments(); |
| 94 | + } |
85 | 95 |
|
86 | 96 | if (includeCast) |
87 | 97 | { |
@@ -514,8 +524,7 @@ private static void GenerateBaseODataPathSegments( |
514 | 524 | // the case. |
515 | 525 | odataPath.Clear(); |
516 | 526 |
|
517 | | - IEdmContainedEntitySet containmnent = navigationSource as IEdmContainedEntitySet; |
518 | | - if (containmnent != null) |
| 527 | + if (navigationSource is IEdmContainedEntitySet) |
519 | 528 | { |
520 | 529 | EdmEntityContainer container = new EdmEntityContainer("NS", "Default"); |
521 | 530 | IEdmEntitySet entitySet = new EdmEntitySet(container, navigationSource.Name, |
@@ -550,5 +559,107 @@ private static void GenerateBaseODataPathSegmentsForFeed( |
550 | 559 | feedContext.EntitySetBase, |
551 | 560 | odataPath); |
552 | 561 | } |
| 562 | + |
| 563 | + private static IList<ODataPathSegment> GenerateContainmentODataPathSegments(this ResourceContext resourceContext) |
| 564 | + { |
| 565 | + List<ODataPathSegment> navigationPathSegments = new List<ODataPathSegment>(); |
| 566 | + ResourceContext currentResourceContext = resourceContext; |
| 567 | + |
| 568 | + // We loop till the base of the $expand expression then use GenerateBaseODataPathSegments to generate the base path segments |
| 569 | + // For instance, given $expand=Tabs($expand=Items($expand=Notes($expand=Tips))), we loop until we get to Tabs at the base |
| 570 | + while (currentResourceContext != null && currentResourceContext.NavigationSource != resourceContext.InternalRequest.Context.Path.NavigationSource) |
| 571 | + { |
| 572 | + if (currentResourceContext.NavigationSource is IEdmContainedEntitySet containedEntitySet) |
| 573 | + { |
| 574 | + // Type-cast segment for the expanded resource that is passed into the method is added by the caller |
| 575 | + if (currentResourceContext != resourceContext && currentResourceContext.StructuredType != containedEntitySet.EntityType()) |
| 576 | + { |
| 577 | + navigationPathSegments.Add(new TypeSegment(currentResourceContext.StructuredType, currentResourceContext.NavigationSource)); |
| 578 | + } |
| 579 | + |
| 580 | + KeySegment keySegment = new KeySegment( |
| 581 | + ConventionsHelpers.GetEntityKey(currentResourceContext), |
| 582 | + currentResourceContext.StructuredType as IEdmEntityType, |
| 583 | + navigationSource: currentResourceContext.NavigationSource); |
| 584 | + navigationPathSegments.Add(keySegment); |
| 585 | + |
| 586 | + NavigationPropertySegment navPropertySegment = new NavigationPropertySegment( |
| 587 | + containedEntitySet.NavigationProperty, |
| 588 | + containedEntitySet.ParentNavigationSource); |
| 589 | + navigationPathSegments.Add(navPropertySegment); |
| 590 | + } |
| 591 | + else if (currentResourceContext.NavigationSource is IEdmEntitySet entitySet) |
| 592 | + { |
| 593 | + // We will get here if there's a non-contained entity set on the $expand expression |
| 594 | + if (currentResourceContext.StructuredType != entitySet.EntityType()) |
| 595 | + { |
| 596 | + navigationPathSegments.Add(new TypeSegment(currentResourceContext.StructuredType, currentResourceContext.NavigationSource)); |
| 597 | + } |
| 598 | + |
| 599 | + KeySegment keySegment = new KeySegment( |
| 600 | + ConventionsHelpers.GetEntityKey(currentResourceContext), |
| 601 | + currentResourceContext.StructuredType as IEdmEntityType, |
| 602 | + currentResourceContext.NavigationSource); |
| 603 | + navigationPathSegments.Add(keySegment); |
| 604 | + |
| 605 | + EntitySetSegment entitySetSegment = new EntitySetSegment(entitySet); |
| 606 | + navigationPathSegments.Add(entitySetSegment); |
| 607 | + |
| 608 | + // Reverse the list such that the segments are in the right order |
| 609 | + navigationPathSegments.Reverse(); |
| 610 | + return navigationPathSegments; |
| 611 | + } |
| 612 | + else if (currentResourceContext.NavigationSource is IEdmSingleton singleton) |
| 613 | + { |
| 614 | + // We will get here if there's a singleton on the $expand expression |
| 615 | + if (currentResourceContext.StructuredType != singleton.EntityType()) |
| 616 | + { |
| 617 | + navigationPathSegments.Add(new TypeSegment(currentResourceContext.StructuredType, currentResourceContext.NavigationSource)); |
| 618 | + } |
| 619 | + |
| 620 | + SingletonSegment singletonSegment = new SingletonSegment(singleton); |
| 621 | + navigationPathSegments.Add(singletonSegment); |
| 622 | + |
| 623 | + // Reverse the list such that the segments are in the right order |
| 624 | + navigationPathSegments.Reverse(); |
| 625 | + return navigationPathSegments; |
| 626 | + } |
| 627 | + |
| 628 | + currentResourceContext = currentResourceContext.SerializerContext.ExpandedResource; |
| 629 | + } |
| 630 | + |
| 631 | + Debug.Assert(currentResourceContext != null, "currentResourceContext != null"); |
| 632 | + // Once we are at the base of the $expand expression, we call GenerateBaseODataPathSegments to generate the base path segments |
| 633 | + IList<ODataPathSegment> pathSegments = currentResourceContext.GenerateBaseODataPathSegments(); |
| 634 | + |
| 635 | + Debug.Assert(pathSegments.Count > 0, "pathSegments.Count > 0"); |
| 636 | + |
| 637 | + ODataPathSegment lastNonKeySegment; |
| 638 | + |
| 639 | + if (pathSegments.Count == 1) |
| 640 | + { |
| 641 | + lastNonKeySegment = pathSegments[0]; |
| 642 | + Debug.Assert(lastNonKeySegment is SingletonSegment, "lastNonKeySegment is SingletonSegment"); |
| 643 | + } |
| 644 | + else |
| 645 | + { |
| 646 | + Debug.Assert(pathSegments[pathSegments.Count - 1] is KeySegment, "pathSegments[pathSegments.Count - 1] is KeySegment"); |
| 647 | + // 2nd last segment would be NavigationPathSegment or EntitySetSegment |
| 648 | + lastNonKeySegment = pathSegments[pathSegments.Count - 2]; |
| 649 | + } |
| 650 | + |
| 651 | + if (currentResourceContext.StructuredType != lastNonKeySegment.EdmType.AsElementType()) |
| 652 | + { |
| 653 | + pathSegments.Add(new TypeSegment(currentResourceContext.StructuredType, currentResourceContext.NavigationSource)); |
| 654 | + } |
| 655 | + |
| 656 | + // Add the segments from the $expand expression in reverse order |
| 657 | + for (int i = navigationPathSegments.Count - 1; i >= 0; i--) |
| 658 | + { |
| 659 | + pathSegments.Add(navigationPathSegments[i]); |
| 660 | + } |
| 661 | + |
| 662 | + return pathSegments; |
| 663 | + } |
553 | 664 | } |
554 | 665 | } |
0 commit comments