diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataResourceDeserializerHelpers.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataResourceDeserializerHelpers.cs index ba3b2b974e..8a987ed026 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataResourceDeserializerHelpers.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataResourceDeserializerHelpers.cs @@ -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; @@ -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) { @@ -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 segments = new List(); + + 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)); + } + /// /// It builds a nested deserializer context from the current deserializer context /// diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Deserialization/ODataResourceDeserializerTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Deserialization/ODataResourceDeserializerTests.cs index 7b680f3dd6..6af5840c59 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Deserialization/ODataResourceDeserializerTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Deserialization/ODataResourceDeserializerTests.cs @@ -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("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 ) }) }; @@ -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[] {new KeyValuePair("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() })); + 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() { diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Resources/ProductsCsdl.xml b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Resources/ProductsCsdl.xml index 4457b9fa98..4733e73e15 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Resources/ProductsCsdl.xml +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Resources/ProductsCsdl.xml @@ -45,7 +45,8 @@ - + + @@ -65,7 +66,8 @@ Concurrency - + +