From 3bbc7d99a9e0137811c7cff0508a2f2d5043cb9d Mon Sep 17 00:00:00 2001 From: Michael Pizzo Date: Sat, 4 Feb 2023 15:28:00 -0800 Subject: [PATCH] Fixes #2737: Support getting navigation source for complex types (#2744) Merging, as no substantive changes were made since Sam's approval. --- .../ODataResourceDeserializerHelpers.cs | 51 +++- .../ODataResourceDeserializerTests.cs | 279 ++++++++++++++++-- .../Resources/ProductsCsdl.xml | 21 +- 3 files changed, 326 insertions(+), 25 deletions(-) diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataResourceDeserializerHelpers.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataResourceDeserializerHelpers.cs index ba3b2b974e..bff282b0a0 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataResourceDeserializerHelpers.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataResourceDeserializerHelpers.cs @@ -66,7 +66,6 @@ internal static ODataDeserializerContext GenerateNestedReadContext(ODataNestedRe segmentType = readContext.Model.FindType(propertyTypeName); } - // could it be a problem later that the navigationSource is null? DynamicPathSegment pathSegment = new DynamicPathSegment( nestedResourceInfo.Name, segmentType, @@ -79,12 +78,12 @@ internal static ODataDeserializerContext GenerateNestedReadContext(ODataNestedRe { if (edmProperty.PropertyKind == EdmPropertyKind.Navigation) { - 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); + IEdmNavigationSource navigationSource = parentNavigationSource?.FindNavigationTarget(navigationProperty, bindingPath); - if (navigationProperty.ContainsTarget) + if (navigationProperty.ContainsTarget || navigationSource == null || navigationSource is IEdmUnknownEntitySet) { path = AppendToPath(path, new NavigationPropertySegment(navigationProperty, navigationSource), navigationProperty.DeclaringType, parentNavigationSource); } @@ -110,6 +109,44 @@ 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, IEdmNavigationProperty navigationProperty) + { + Contract.Assert(navigationProperty != null, "Called GetBindingPath with a null navigation property"); + if (path == null) + { + return null; + } + + // Binding Path is made up of complex types, containment navigation properties, and type segments + List segments = new List(); + foreach (ODataPathSegment segment in path.Segments) + { + if (segment is NavigationPropertySegment navSegment) + { + 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); + } + } + + if(navigationProperty.DeclaringType != path.EdmType as IEdmStructuredType) + { + // Add a type cast segment + segments.Add(navigationProperty.DeclaringType.FullTypeName()); + } + + segments.Add(navigationProperty.Name); + + return new EdmPathExpression(String.Join("/", segments)); + } + /// /// It builds a nested deserializer context from the current deserializer context /// @@ -154,11 +191,13 @@ internal static Routing.ODataPath AppendToPath(Routing.ODataPath path, ODataPath } List segments = new List(path.Segments); + IEdmType pathType = path.EdmType; // Append type cast segment if required - if (declaringType != null && path.EdmType != declaringType) + if (declaringType != null && pathType != null && pathType != declaringType + && declaringType.IsOrInheritsFrom(pathType.AsElementType())) { - segments.Add(new TypeSegment(declaringType, navigationSource)); + segments.Add(new TypeSegment(declaringType, pathType, navigationSource)); } segments.Add(segment); 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..3809dcecea 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 @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.IO; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; @@ -15,7 +16,9 @@ using Microsoft.AspNet.OData.Builder; using Microsoft.AspNet.OData.Formatter; using Microsoft.AspNet.OData.Formatter.Deserialization; +using Microsoft.AspNet.OData.Formatter.Serialization; using Microsoft.AspNet.OData.Test.Abstraction; +using Microsoft.AspNet.OData.Test.Builder; using Microsoft.AspNet.OData.Test.Common; using Microsoft.AspNet.OData.Test.Common.Types; using Microsoft.OData; @@ -31,6 +34,7 @@ public class ODataResourceDeserializerTests { private readonly IEdmModel _edmModel; private readonly ODataDeserializerContext _readContext; + private readonly ODataDeserializerContext _supplierContext; private readonly IEdmEntityTypeReference _productEdmType; private readonly IEdmEntityTypeReference _supplierEdmType; private readonly IEdmComplexTypeReference _addressEdmType; @@ -40,16 +44,25 @@ public ODataResourceDeserializerTests() { _edmModel = EdmTestHelpers.GetModel(); IEdmEntitySet entitySet = _edmModel.EntityContainer.FindEntitySet("Products"); + IEdmEntitySet suppliersEntitySet = _edmModel.EntityContainer.FindEntitySet("Suppliers"); + _productEdmType = _edmModel.GetEdmTypeReference(typeof(Product)).AsEntity(); + _supplierEdmType = _edmModel.GetEdmTypeReference(typeof(Supplier)).AsEntity(); + _addressEdmType = _edmModel.GetEdmTypeReference(typeof(Address)).AsComplex(); + _deserializerProvider = ODataDeserializerProviderFactory.Create(); + _readContext = new ODataDeserializerContext { Path = new ODataPath(new EntitySetSegment(entitySet)), Model = _edmModel, ResourceType = typeof(Product) }; - _productEdmType = _edmModel.GetEdmTypeReference(typeof(Product)).AsEntity(); - _supplierEdmType = _edmModel.GetEdmTypeReference(typeof(Supplier)).AsEntity(); - _addressEdmType = _edmModel.GetEdmTypeReference(typeof(Address)).AsComplex(); - _deserializerProvider = ODataDeserializerProviderFactory.Create(); + + _supplierContext = new ODataDeserializerContext + { + Model = _edmModel, + Path = new ODataPath(new EntitySetSegment(suppliersEntitySet), new KeySegment(new KeyValuePair[] { new KeyValuePair("ID", 7) }, suppliersEntitySet.EntityType(), suppliersEntitySet)), + Request = RequestFactory.Create() + }; } [Fact] @@ -1264,8 +1277,12 @@ public void ApplyNestedProperty_UsesThePropertyAlias_ForResourceWrapper() model.Order.FindProperty("Customer"), new ClrPropertyInfoAnnotation(typeof(Order).GetProperty("AliasedCustomer"))); ODataResource resource = new ODataResource { Properties = new[] { new ODataProperty { Name = "ID", Value = 42 } } }; + ODataResourceSet resourceSet = new ODataResourceSet(); Order order = new Order(); + ODataNestedResourceInfoWrapper ordersInfoWrapper = + new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo { Name = "Orders" }); + ordersInfoWrapper.NestedItems.Add(new ODataResourceSetWrapper(resourceSet)); ODataNestedResourceInfoWrapper resourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo { Name = "Customer" }); resourceInfoWrapper.NestedItems.Add(new ODataResourceWrapper(resource)); @@ -1274,21 +1291,28 @@ public void ApplyNestedProperty_UsesThePropertyAlias_ForResourceWrapper() IEdmEntityType entityType1 = customerTypeReference.EntityDefinition(); EdmEntityContainer container = new EdmEntityContainer("NS", "Container"); - IEdmNavigationSource navigationSource = new EdmEntitySet(container, "EntitySet", entityType1); + EdmEntitySet navigationSource = new EdmEntitySet(container, "EntitySet", entityType1); + EdmEntitySet ordersSource = new EdmEntitySet(container, "Orders", model.Order); + + IEdmNavigationProperty ordersNavigation = entityType1.NavigationProperties().First(n => n.Name == "Orders"); + navigationSource.AddNavigationTarget(ordersNavigation, ordersSource); 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 ) }) }; + ODataDeserializerContext ordersNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(ordersInfoWrapper, readContext, ordersNavigation); + // Act new ODataResourceDeserializer(_deserializerProvider) - .ApplyNestedProperty(order, resourceInfoWrapper, model.Order.AsReference(), readContext); + .ApplyNestedProperty(order, resourceInfoWrapper, model.Order.AsReference(), ordersNestedContext); // Assert Assert.Equal(42, order.AliasedCustomer.ID); @@ -1350,7 +1374,7 @@ public void ApplyNestedProperties_Preserves_ReadContextRequest() var originalContext = new ODataDeserializerContext { Model = _edmModel, - Path = _readContext.Path, + Path = _supplierContext.Path, Request = RequestFactory.Create() }; @@ -1588,20 +1612,17 @@ public void Read_ThrowsOnUnknownEntityType() public void GenerateNestedReadContext_Generates_NestedDeserializerContext() { //Arrange - var currentContext = new ODataDeserializerContext - { - Model = _edmModel, - Path = _readContext.Path, - Request = RequestFactory.Create() - }; - IEdmEntitySet productsEntitySet = _edmModel.EntityContainer.FindEntitySet("Products"); - ODataPath currentPath = new ODataPath(new EntitySetSegment(productsEntitySet)); IEdmEntitySet suppliersEntitySet = _edmModel.EntityContainer.FindEntitySet("Suppliers"); ODataPath expectedOdataPath = new ODataPath(new EntitySetSegment(suppliersEntitySet)); - Assert.Equal(currentPath.ToString(), currentContext.Path.ToString()); + var currentContext = new ODataDeserializerContext + { + Model = _edmModel, + Path = new ODataPath(new EntitySetSegment(productsEntitySet), new KeySegment(new KeyValuePair[] { new KeyValuePair("ID", 7) }, productsEntitySet.EntityType(), productsEntitySet)), + Request = RequestFactory.Create() + }; ODataNestedResourceInfoWrapper nestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Supplier" }); nestedResourceInfoWrapper.NestedItems.Add(new ODataResourceWrapper(new ODataResource { Properties = new List() })); @@ -1616,6 +1637,230 @@ 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"); + IEdmEntityTypeReference supplierTypeReference = _edmModel.GetEdmTypeReference(typeof(Supplier)).AsEntity(); + IEdmProperty addressProperty = supplierTypeReference.FindProperty("Address"); + IEdmComplexTypeReference addressTypeReference = _edmModel.GetEdmTypeReference(typeof(Address)).AsComplex(); + IEdmProperty suppliersProperty = addressTypeReference.FindProperty("Suppliers"); + + ODataNestedResourceInfoWrapper addressNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Address" }); + addressNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceWrapper(new ODataResource { Properties = new List() })); + ODataNestedResourceInfoWrapper suppliersNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Suppliers" }); + suppliersNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceSetWrapper(new ODataResourceSet())); + + + ODataPath expectedOdataPath = new ODataPath(new EntitySetSegment(suppliersEntitySet)); + + var currentContext = new ODataDeserializerContext + { + Model = _edmModel, + Path = _supplierContext.Path, + Request = RequestFactory.Create(), + }; + + //Act + ODataDeserializerContext addressNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(addressNestedResourceInfoWrapper, currentContext, addressProperty); + ODataDeserializerContext suppliersNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(suppliersNestedResourceInfoWrapper, addressNestedContext, suppliersProperty); + + ///Assert + Assert.NotNull(suppliersNestedContext.Path); + Assert.Equal(expectedOdataPath.ToString(), suppliersNestedContext.Path.ToString()); + } + + [Fact] + public void GenerateNestedReadContext_Generates_NestedDeserializerContextForUnboundNavigationProperty() + { + // Arrange + IEdmEntitySet suppliersEntitySet = _edmModel.EntityContainer.FindEntitySet("Suppliers"); + IEdmEntityTypeReference supplierTypeReference = _edmModel.GetEdmTypeReference(typeof(Supplier)).AsEntity(); + IEdmStructuralProperty addressProperty = supplierTypeReference.FindProperty("Address") as IEdmStructuralProperty; + IEdmComplexTypeReference addressTypeReference = _edmModel.GetEdmTypeReference(typeof(Address)).AsComplex(); + IEdmNavigationProperty suppliersProperty = addressTypeReference.FindNavigationProperty("UnboundSuppliers"); + KeySegment suppliersKeySegment = new KeySegment(new KeyValuePair[] { new KeyValuePair("ID", 7) }, suppliersEntitySet.EntityType(), suppliersEntitySet); + + // Suppliers(7)/Address/UnboundSuppliers + ODataPath expectedOdataPath = new ODataPath( + new EntitySetSegment(suppliersEntitySet), + suppliersKeySegment, + new PropertySegment(addressProperty), + new NavigationPropertySegment(suppliersProperty, null) + ); + + ODataNestedResourceInfoWrapper addressNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Address" }); + addressNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceWrapper(new ODataResource { Properties = new List() })); + ODataNestedResourceInfoWrapper suppliersNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "UnboundSuppliers" }); + suppliersNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceSetWrapper(new ODataResourceSet())); + + var currentContext = new ODataDeserializerContext + { + Model = _edmModel, + Path = _supplierContext.Path, + Request = RequestFactory.Create(), + }; + + // Act + ODataDeserializerContext addressNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(addressNestedResourceInfoWrapper, currentContext, addressProperty); + ODataDeserializerContext suppliersNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(suppliersNestedResourceInfoWrapper, addressNestedContext, suppliersProperty); + + // Assert + Assert.NotNull(suppliersNestedContext.Path); + Assert.Equal(expectedOdataPath.ToString(), suppliersNestedContext.Path.ToString()); + } + + [Fact] + public void GenerateNestedReadContext_Generates_NestedDeserializerContextForDerivedComplexType() + { + // Arrange + IEdmEntitySet suppliersEntitySet = _edmModel.EntityContainer.FindEntitySet("Suppliers"); + IEdmEntityTypeReference supplierTypeReference = _edmModel.GetEdmTypeReference(typeof(Supplier)).AsEntity(); + IEdmProperty addressProperty = supplierTypeReference.FindProperty("Address"); + IEdmComplexType derivedAddressType = _edmModel.FindType("ODataDemo.DerivedAddress") as IEdmComplexType; + IEdmProperty derivedSuppliersProperty = derivedAddressType.FindProperty("DerivedSuppliers"); + KeySegment suppliersKeySegment = new KeySegment(new KeyValuePair[] { new KeyValuePair("ID", 7) }, suppliersEntitySet.EntityType(), suppliersEntitySet); + + ODataNestedResourceInfoWrapper addressNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Address" }); + addressNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceWrapper(new ODataResource { Properties = new List(), TypeName = "ODataDemo.DerivedAddress", TypeAnnotation = new ODataTypeAnnotation("ODataDemo.DerivedAddress") })); + ODataNestedResourceInfoWrapper suppliersNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "DerivedSuppliers" }); + suppliersNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceSetWrapper(new ODataResourceSet())); + + ODataPath expectedOdataPath = new ODataPath(new EntitySetSegment(suppliersEntitySet)); + + var currentContext = new ODataDeserializerContext + { + Model = _edmModel, + Path = new ODataPath(new EntitySetSegment(suppliersEntitySet), suppliersKeySegment), + Request = RequestFactory.Create(), + }; + + // Act + ODataDeserializerContext addressNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(addressNestedResourceInfoWrapper, currentContext, addressProperty); + ODataDeserializerContext derivedSuppliersNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(suppliersNestedResourceInfoWrapper, addressNestedContext, derivedSuppliersProperty); + + // Assert + Assert.NotNull(derivedSuppliersNestedContext.Path); + Assert.Equal(expectedOdataPath.ToString(), derivedSuppliersNestedContext.Path.ToString()); + } + + [Fact] + public void GenerateNestedReadContext_Generates_NestedDeserializerContextOnDynamicType() + { + // Arrange + IEdmEntitySet suppliersEntitySet = _edmModel.EntityContainer.FindEntitySet("Suppliers"); + IEdmComplexTypeReference addressTypeReference = _edmModel.GetEdmTypeReference(typeof(Address)).AsComplex(); + IEdmNavigationProperty suppliersProperty = addressTypeReference.FindNavigationProperty("Suppliers"); + KeySegment suppliersKeySegment = new KeySegment(new KeyValuePair[] { new KeyValuePair("ID", 7) }, suppliersEntitySet.EntityType(), suppliersEntitySet); + + // Suppliers(7)/Dynamic/Suppliers + ODataPath expectedOdataPath = new ODataPath( + new EntitySetSegment(suppliersEntitySet), + suppliersKeySegment, + new DynamicPathSegment("Dynamic"), + new NavigationPropertySegment(suppliersProperty, null) + ); + + ODataNestedResourceInfoWrapper dynamicNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Dynamic", TypeAnnotation = new ODataTypeAnnotation("ODataDemo.Address"), IsCollection = false }); + dynamicNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceWrapper(new ODataResource { Properties = new List(), TypeName = "ODataDemo.Address" })); + ODataNestedResourceInfoWrapper supplierNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Suppliers" }); + supplierNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceSetWrapper(new ODataResourceSet())); + + // Suppliers(7) + var currentContext = new ODataDeserializerContext + { + Model = _edmModel, + Path = new ODataPath(new EntitySetSegment(suppliersEntitySet), suppliersKeySegment), + Request = RequestFactory.Create(), + }; + + // Act + // Dynamic + ODataDeserializerContext dynamicNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(dynamicNestedResourceInfoWrapper, currentContext, null); + // Supplier + ODataDeserializerContext suppliersNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(supplierNestedResourceInfoWrapper, dynamicNestedContext, suppliersProperty); + + // Assert + Assert.NotNull(suppliersNestedContext.Path); + Assert.Equal(expectedOdataPath.ToString(), suppliersNestedContext.Path.ToString()); + } + + [Fact(Skip = "Navigation property bindings ending in cast segments not yet fully supported.")] + public void GenerateNestedReadContext_Generates_NestedDeserializerContextWithSingleBindingEndingInCastSegment() + { + // Arrange + IEdmEntitySet suppliersEntitySet = _edmModel.EntityContainer.FindEntitySet("Suppliers"); + IEdmEntitySet productsEntitySet = _edmModel.EntityContainer.FindEntitySet("Products"); + IEdmEntitySet preferredProductsEntitySet = _edmModel.EntityContainer.FindEntitySet("PreferredProducts"); + IEdmEntityTypeReference supplierTypeReference = _edmModel.GetEdmTypeReference(typeof(Supplier)).AsEntity(); + IEdmNavigationProperty productsProperty = supplierTypeReference.FindNavigationProperty("Products"); + + ODataNestedResourceInfoWrapper productsNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Products" }); + productsNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceWrapper(new ODataResource { Properties = new List(), TypeAnnotation = new ODataTypeAnnotation("ODataDemo.Product") })); + ODataNestedResourceInfoWrapper preferredProductsNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Products" }); + preferredProductsNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceWrapper(new ODataResource { Properties = new List(), TypeAnnotation = new ODataTypeAnnotation("ODataDemo.PreferredProduct") })); + + ODataPath expectedOdataPath = new ODataPath(new EntitySetSegment(productsEntitySet)); + ODataPath expectedPreferredOdataPath = new ODataPath(new EntitySetSegment(preferredProductsEntitySet)); + + var currentContext = new ODataDeserializerContext + { + Model = _edmModel, + Path = _supplierContext.Path, + Request = RequestFactory.Create(), + }; + + // Act + ODataDeserializerContext productsNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(productsNestedResourceInfoWrapper, currentContext, productsProperty); + ODataDeserializerContext preferredProductsNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(preferredProductsNestedResourceInfoWrapper, currentContext, productsProperty); + ODataDeserializerContext preferredProductNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(preferredProductsNestedResourceInfoWrapper, preferredProductsNestedContext, productsProperty); + + // Assert + Assert.NotNull(productsNestedContext.Path); + Assert.Equal(expectedOdataPath.ToString(), productsNestedContext.Path.ToString()); + Assert.NotNull(preferredProductsNestedContext.Path); + Assert.Equal(expectedPreferredOdataPath.ToString(), preferredProductsNestedContext.Path.ToString()); + } + + [Fact(Skip = "Bindings ending in cast segments not fully supported yet.")] + public void GenerateNestedReadContext_Generates_NestedDeserializerContextWithBindingEndingInCastSegment() + { + // Arrange + IEdmEntitySet suppliersEntitySet = _edmModel.EntityContainer.FindEntitySet("Suppliers"); + IEdmEntitySet preferredSuppliersEntitySet = _edmModel.EntityContainer.FindEntitySet("PreferredSuppliers"); + IEdmEntityTypeReference supplierTypeReference = _edmModel.GetEdmTypeReference(typeof(Supplier)).AsEntity(); + IEdmProperty addressProperty = supplierTypeReference.FindProperty("Address"); + IEdmComplexTypeReference addressTypeReference = _edmModel.GetEdmTypeReference(typeof(Address)).AsComplex(); + IEdmProperty suppliersProperty = addressTypeReference.FindProperty("Suppliers"); + + ODataNestedResourceInfoWrapper addressNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Address" }); + addressNestedResourceInfoWrapper.NestedItems.Add(new ODataResourceWrapper(new ODataResource { Properties = new List() })); + ODataNestedResourceInfoWrapper suppliersNestedResourceInfoWrapper = new ODataNestedResourceInfoWrapper(new ODataNestedResourceInfo() { Name = "Suppliers" }); + ODataResourceSetWrapper suppliersNestedResourceSetWrapper = new ODataResourceSetWrapper(new ODataResourceSet()); + suppliersNestedResourceSetWrapper.Resources.Add(new ODataResourceWrapper(new ODataResource { Properties = new List(), TypeAnnotation = new ODataTypeAnnotation("ODataDemo.PreferredSupplier") })); + + ODataPath expectedOdataPath = new ODataPath(new EntitySetSegment(preferredSuppliersEntitySet)); + + var currentContext = new ODataDeserializerContext + { + Model = _edmModel, + Path = _supplierContext.Path, + Request = RequestFactory.Create(), + }; + + ODataDeserializerContext addressNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(addressNestedResourceInfoWrapper, currentContext, addressProperty); + ODataDeserializerContext suppliersNestedContext = ODataResourceDeserializerHelpers.GenerateNestedReadContext(suppliersNestedResourceInfoWrapper, addressNestedContext, suppliersProperty); + + // Act + ODataResourceSetDeserializer deserializer = new ODataResourceSetDeserializer(ODataDeserializerProviderFactory.Create()); + deserializer.ReadInline(suppliersNestedResourceSetWrapper, supplierTypeReference, suppliersNestedContext); + + // Assert + Assert.NotNull(suppliersNestedContext.Path); + Assert.Equal(expectedOdataPath.ToString(), suppliersNestedContext.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..e1a3881a4e 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Resources/ProductsCsdl.xml +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Resources/ProductsCsdl.xml @@ -20,6 +20,7 @@ + @@ -39,12 +40,20 @@ + + + + + + + + @@ -58,14 +67,22 @@ - + + + Concurrency - + + + + + + +