Skip to content

Commit

Permalink
Fixes #2737: Support getting navigation source for navigation propert…
Browse files Browse the repository at this point in the history
…ies on complex types
  • Loading branch information
mikepizzo committed Jan 7, 2023
1 parent d8c66b6 commit 817a509
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Interfaces;
using Microsoft.OData;
using Microsoft.OData.Edm;
Expand Down Expand Up @@ -82,7 +84,8 @@ internal static ODataDeserializerContext GenerateNestedReadContext(ODataNestedRe
Contract.Assert(readContext.Path.NavigationSource != null, "Navigation property segment with null navigationSource");
IEdmNavigationProperty navigationProperty = edmProperty as IEdmNavigationProperty;
IEdmNavigationSource parentNavigationSource = readContext.Path.NavigationSource;
IEdmNavigationSource navigationSource = parentNavigationSource.FindNavigationTarget(navigationProperty);
IEdmPathExpression bindingPath = GetBindingPath(readContext.Path, navigationProperty.Name);
IEdmNavigationSource navigationSource = parentNavigationSource.FindNavigationTarget(navigationProperty, bindingPath);

if (navigationProperty.ContainsTarget)
{
Expand Down Expand Up @@ -110,6 +113,39 @@ internal static ODataDeserializerContext GenerateNestedReadContext(ODataNestedRe
return BuildNestedContextFromCurrentContext(readContext, path);
}

// Determines the binding path for an OData Path to a given navigationProperty
private static IEdmPathExpression GetBindingPath(Routing.ODataPath path, string navPropName)
{
List<string> segments = new List<string>();

if (path == null)
{
return null;
}

// Binding Path is made up of complex types, containment navigation properties, and type segments
foreach (ODataPathSegment segment in path.Segments)
{
if (segment is NavigationPropertySegment navSegment)
{
Debug.Assert(navSegment.NavigationProperty.ContainsTarget, "Non-contained navigation property in binding path");
segments.Add(navSegment.NavigationProperty.Name);
}
else if (segment is PropertySegment propertySegment)
{
segments.Add(propertySegment.Property.Name);
}
else if (segment is TypeSegment typeSegment)
{
segments.Add(typeSegment.Identifier);
}
}

segments.Add(navPropName);

return new EdmPathExpression(String.Join("/", segments));
}

/// <summary>
/// It builds a nested deserializer context from the current deserializer context
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1274,14 +1274,15 @@ public void ApplyNestedProperty_UsesThePropertyAlias_ForResourceWrapper()

IEdmEntityType entityType1 = customerTypeReference.EntityDefinition();
EdmEntityContainer container = new EdmEntityContainer("NS", "Container");
IEdmNavigationSource navigationSource = new EdmEntitySet(container, "EntitySet", entityType1);
IEdmEntitySet navigationSource = new EdmEntitySet(container, "EntitySet", entityType1);

var keys = new[] { new KeyValuePair<string, object>("ID", 42) };

ODataDeserializerContext readContext = new ODataDeserializerContext()
{
Model = model.Model,
Path = new ODataPath(new ODataPathSegment[1] {
Path = new ODataPath(new ODataPathSegment[2] {
new EntitySetSegment(navigationSource),
new KeySegment(keys, entityType1, navigationSource )
})
};
Expand Down Expand Up @@ -1616,6 +1617,39 @@ public void GenerateNestedReadContext_Generates_NestedDeserializerContext()
Assert.Equal(expectedOdataPath.ToString(), nestedContext.Path.ToString());
}

[Fact]
public void GenerateNestedReadContext_Generates_NestedDeserializerContextForComplexType()
{
//Arrange
IEdmEntitySet suppliersEntitySet = _edmModel.EntityContainer.FindEntitySet("Suppliers");
ODataPath expectedOdataPath = new ODataPath(new EntitySetSegment(suppliersEntitySet));

var currentContext = new ODataDeserializerContext
{
Model = _edmModel,
Path = new ODataPath(new EntitySetSegment(suppliersEntitySet), new KeySegment(new KeyValuePair<string, object>[] {new KeyValuePair<string, object>("ID", 7)}, suppliersEntitySet.EntityType(), suppliersEntitySet)),
Request = RequestFactory.Create(),
};

ODataNestedResourceInfoWrapper addressNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Address" });
addressNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceWrapper(new ODataResource { Properties = new List<ODataProperty>() }));
ODataNestedResourceInfoWrapper supplierNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Suppliers" });
supplierNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceSetWrapper(new ODataResourceSet()));

IEdmEntityTypeReference supplierTypeReference = _edmModel.GetEdmTypeReference(typeof(Supplier)).AsEntity();
IEdmProperty addressProperty = supplierTypeReference.FindProperty("Address");
IEdmComplexTypeReference addressTypeReference = _edmModel.GetEdmTypeReference(typeof(Address)).AsComplex();
IEdmProperty suppliersProperty = addressTypeReference.FindProperty("Suppliers");

//Act
ODataDeserializerContext addressNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(addressNestedResourceInfoWrapper, currentContext, addressProperty);
ODataDeserializerContext supplierNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(addressNestedResourceInfoWrapper, addressNestedContext, suppliersProperty);

///Assert
Assert.NotNull(supplierNestedContext.Path);
Assert.Equal(expectedOdataPath.ToString(), supplierNestedContext.Path.ToString());
}

[Fact]
public void ApplyIdToPath_CreatesODataPathWithNullKeySegment_IfKeyValueNotSet()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
<Property Name="State" Type="Edm.String" />
<Property Name="ZipCode" Type="Edm.String" />
<Property Name="CountryOrRegion" Type="Edm.String" />
</ComplexType>
<NavigationProperty Name="Suppliers" Type="Collection(ODataDemo.Supplier)"/>
</ComplexType>
<Function Name="GetProductsByRating" m:HttpMethod="GET">
<ReturnType Type="Collection(ODataDemo.Product)" />
<Parameter Name="rating" Type="Edm.Int32" Nullable="false" />
Expand All @@ -65,7 +66,8 @@
<PropertyPath>Concurrency</PropertyPath>
</Collection>
</Annotation>
</EntitySet>
<NavigationPropertyBinding Path="Address/Suppliers" Target="Suppliers" />
</EntitySet>
<FunctionImport Name="GetProductsByRating" Function="ODataDemo.GetProductsByRating" EntitySet="Products" IncludeInServiceDocument="true" />
</EntityContainer>
</Schema>
Expand Down

0 comments on commit 817a509

Please sign in to comment.