diff --git a/src/Microsoft.AspNetCore.OData/Edm/NavigationSourceLinkBuilderAnnotation.cs b/src/Microsoft.AspNetCore.OData/Edm/NavigationSourceLinkBuilderAnnotation.cs index 2ed259f23..4d9c78a91 100644 --- a/src/Microsoft.AspNetCore.OData/Edm/NavigationSourceLinkBuilderAnnotation.cs +++ b/src/Microsoft.AspNetCore.OData/Edm/NavigationSourceLinkBuilderAnnotation.cs @@ -60,12 +60,10 @@ public NavigationSourceLinkBuilderAnnotation(IEdmNavigationSource navigationSour } // Add navigation link builders for all navigation properties in derived types. - bool derivedTypesDefineNavigationProperty = false; foreach (IEdmEntityType derivedEntityType in derivedTypes) { foreach (IEdmNavigationProperty navigationProperty in derivedEntityType.DeclaredNavigationProperties()) { - derivedTypesDefineNavigationProperty = true; Func navigationLinkFactory = (resourceContext, navProperty) => resourceContext.GenerateNavigationPropertyLink(navProperty, includeCast: true); AddNavigationPropertyLinkBuilder(navigationProperty, new NavigationLinkBuilder(navigationLinkFactory, followsConventions: true)); @@ -73,7 +71,7 @@ public NavigationSourceLinkBuilderAnnotation(IEdmNavigationSource navigationSour } Func selfLinkFactory = - (resourceContext) => resourceContext.GenerateSelfLink(includeCast: derivedTypesDefineNavigationProperty); + (resourceContext) => resourceContext.GenerateSelfLink(includeCast: false); IdLinkBuilder = new SelfLinkBuilder(selfLinkFactory, followsConventions: true); } diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesControllers.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesControllers.cs index fc92a76a3..ba1c7c482 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesControllers.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesControllers.cs @@ -47,6 +47,20 @@ static CustomersController() { new Order { Id = 4, Amount = 170M } } + }, + new GoldCustomer { + Id = 4, + Name = "Customer 4", + LoyaltyCardNo = "9876543211", + Orders = new List + { + new Order { Id = 5, Amount = 2230M }, + new Order { Id = 6, Amount = 1150M } + }, + BulkOrders = new List + { + new Order { Id = 7, Amount = 66832M } + } } }; } @@ -59,6 +73,7 @@ public IActionResult Get() [EnableQuery] [HttpGet("odata/Customers/Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.VipCustomer({key})")] // convention routing doesn't create this template. + [HttpGet("odata/Customers/Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.GoldCustomer({key})")] // convention routing doesn't create this template. public IActionResult Get(int key) { var customer = Customers.FirstOrDefault(c => c.Id == key); @@ -91,5 +106,26 @@ public IActionResult GetVipCustomer([FromODataUri] int key) return Ok(vipCustomer); } + + // Handles /entityset/cast path template + [EnableQuery] + public IActionResult GetFromGoldCustomer() + { + return Ok(Customers.OfType()); + } + + // Handles /entityset/key/cast and /entityset/cast/key path templates + [EnableQuery] + public IActionResult GetGoldCustomer([FromODataUri] int key) + { + var vipCustomer = Customers.OfType().SingleOrDefault(d => d.Id.Equals(key)); + + if (vipCustomer == null) + { + return NotFound(); + } + + return Ok(vipCustomer); + } } } diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesDataModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesDataModel.cs index c978dc260..4016afa65 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesDataModel.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesDataModel.cs @@ -21,6 +21,11 @@ public class VipCustomer : Customer public string LoyaltyCardNo { get; set; } } + public class GoldCustomer : VipCustomer + { + public List BulkOrders { get; set; } + } + public class Order { public int Id { get; set; } diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesTests.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesTests.cs index 9ef754530..f9393ce3d 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesTests.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DerivedTypes/DerivedTypesTests.cs @@ -6,6 +6,7 @@ //------------------------------------------------------------------------------ using System.Net.Http; +using System.Net.Http.Headers; using System.Threading.Tasks; using Microsoft.AspNetCore.OData.TestCommon; using Microsoft.Extensions.DependencyInjection; @@ -135,5 +136,37 @@ public async Task RestrictEntityToDerivedTypeInstance_ThenExpandNavProperty(stri "\"Orders\":[{\"Id\":2,\"Amount\":230},{\"Id\":3,\"Amount\":150}]"; Assert.Contains(expectedContent, await response.Content.ReadAsStringAsync()); } + + [Theory] + [InlineData("Customers(4)")] + [InlineData("Customers(4)/Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.GoldCustomer")] + [InlineData("Customers/Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.GoldCustomer(4)")] + public async Task DerivedTypeNavPropertyLink_FullMetadata(string pathAndQuery) + { + // Arrange: Key preceeds name of the derived type + string requestUri = $"/odata/{pathAndQuery}"; + + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri); + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=full")); + HttpClient client = CreateClient(); + + // Act + HttpResponseMessage response = await client.SendAsync(request); + + // Assert + Assert.True(response.IsSuccessStatusCode); + + string expectedContent = "{\"@odata.context\":\"http://localhost/odata/$metadata#Customers/Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.GoldCustomer/$entity\"," + + "\"@odata.type\":\"#Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.GoldCustomer\"," + + "\"@odata.id\":\"http://localhost/odata/Customers(4)\"," + + "\"@odata.editLink\":\"Customers(4)/Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.GoldCustomer\"," + + "\"Id\":4,\"Name\":\"Customer 4\",\"LoyaltyCardNo\":\"9876543211\"," + + "\"Orders@odata.associationLink\":\"http://localhost/odata/Customers(4)/Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.GoldCustomer/Orders/$ref\"," + + "\"Orders@odata.navigationLink\":\"http://localhost/odata/Customers(4)/Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.GoldCustomer/Orders\"," + + "\"BulkOrders@odata.associationLink\":\"http://localhost/odata/Customers(4)/Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.GoldCustomer/BulkOrders/$ref\"," + + "\"BulkOrders@odata.navigationLink\":\"http://localhost/odata/Customers(4)/Microsoft.AspNetCore.OData.E2E.Tests.DerivedTypes.GoldCustomer/BulkOrders\"}"; + + Assert.Equal(expectedContent, await response.Content.ReadAsStringAsync()); + } } }