diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/ODataOutputFormatterHelper.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/ODataOutputFormatterHelper.cs index 43ca8cdfdc..97fa808258 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/ODataOutputFormatterHelper.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/ODataOutputFormatterHelper.cs @@ -351,29 +351,6 @@ private static string GetRootElementName(ODataPath path) return null; } - // This function is used to determine whether an OData path includes operation (import) path segments. - // We use this function to make sure the value of ODataUri.Path in ODataMessageWriterSettings is null - // when any path segment is an operation. ODL will try to calculate the context URL if the ODataUri.Path - // equals to null. - private static bool IsOperationPath(ODataPath path) - { - if (path == null) - { - return false; - } - - foreach (ODataPathSegment segment in path.Segments) - { - if (segment is OperationSegment || - segment is OperationImportSegment) - { - return true; - } - } - - return false; - } - private static Microsoft.OData.UriParser.ODataPath ConvertPath(ODataPath path) { if (path == null) @@ -381,56 +358,7 @@ private static Microsoft.OData.UriParser.ODataPath ConvertPath(ODataPath path) return null; } - if (IsOperationPath(path)) - { - var lastSegment = path.Segments.Last(); - OperationSegment operation = lastSegment as OperationSegment; - if (operation != null && operation.EntitySet != null) - { - return GeneratePath(operation.EntitySet); - } - - OperationImportSegment operationImport = lastSegment as OperationImportSegment; - if (operationImport != null && operationImport.EntitySet != null) - { - return GeneratePath(operationImport.EntitySet); - } - - return null; - } - return path.Path; } - - private static Microsoft.OData.UriParser.ODataPath GeneratePath(IEdmNavigationSource navigationSource) - { - Contract.Assert(navigationSource != null); - - switch (navigationSource.NavigationSourceKind()) - { - case EdmNavigationSourceKind.EntitySet: - return new Microsoft.OData.UriParser.ODataPath(new EntitySetSegment((IEdmEntitySet)navigationSource)); - - case EdmNavigationSourceKind.Singleton: - return new Microsoft.OData.UriParser.ODataPath(new SingletonSegment((IEdmSingleton)navigationSource)); - - case EdmNavigationSourceKind.ContainedEntitySet: - IEdmContainedEntitySet containedEntitySet = (IEdmContainedEntitySet)navigationSource; - Microsoft.OData.UriParser.ODataPath path = GeneratePath(containedEntitySet.ParentNavigationSource); - IList segments = new List(); - foreach (var item in path) - { - segments.Add(item); - } - - segments.Add(new NavigationPropertySegment(containedEntitySet.NavigationProperty, containedEntitySet.ParentNavigationSource)); - return new Microsoft.OData.UriParser.ODataPath(segments); - - case EdmNavigationSourceKind.None: - case EdmNavigationSourceKind.UnknownEntitySet: - default: - return null; - } - } } } diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/UnboundOperation/UnboundOperationTest.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/UnboundOperation/UnboundOperationTest.cs index 007e2e8d07..96d9a2d124 100644 --- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/UnboundOperation/UnboundOperationTest.cs +++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/UnboundOperation/UnboundOperationTest.cs @@ -437,6 +437,56 @@ public async Task ActionImportFollowedByQueryOption() Assert.DoesNotContain("Street 11", responseString); } + [Fact] + public async Task FunctionImportWithExpand() + { + // Arrange + const int CustomerId = 407; + ConventionCustomer expectCustomer = new ConventionCustomersController().GetConventionCustomerById(CustomerId); + Assert.NotNull(expectCustomer); + Assert.NotNull(expectCustomer.Orders); + Assert.NotEmpty(expectCustomer.Orders); + + // Act - Expand Orders navigation property from function import result + var requestUri = this.BaseAddress + "/odata/GetConventionCustomerByIdImport(CustomerId=" + CustomerId + ")?$expand=Orders"; + var response = await Client.GetAsync(requestUri); + var responseString = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.True(response.IsSuccessStatusCode, $"Request failed with status {response.StatusCode}. Response: {responseString}"); + Assert.Contains("/$metadata#ConventionCustomers/$entity", responseString); + + // Verify customer data is present + string expect = "\"ID\":" + expectCustomer.ID; + Assert.Contains(expect, responseString); + expect = "\"Name\":\"" + expectCustomer.Name + "\""; + Assert.Contains(expect, responseString); + + // Verify expanded Orders are present + Assert.Contains("\"Orders\":", responseString); + foreach (var order in expectCustomer.Orders) + { + Assert.Contains("\"OrderName\":\"" + order.OrderName + "\"", responseString); + } + } + + [Fact] + public async Task FunctionImportReturningCollectionWithExpand() + { + // Act - Expand Orders navigation property from function import returning collection + var requestUri = this.BaseAddress + "/odata/GetAllConventionCustomersImport()?$expand=Orders"; + var response = await Client.GetAsync(requestUri); + var responseString = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.True(response.IsSuccessStatusCode, $"Request failed with status {response.StatusCode}. Response: {responseString}"); + Assert.Contains("/$metadata#ConventionCustomers", responseString); + + // Verify that orders are expanded for at least one customer + Assert.Contains("\"Orders\":", responseString); + Assert.Contains("\"OrderName\":", responseString); + } + #endregion } }