Skip to content

Commit

Permalink
Consider the OperationSegment when determining the TargetStructuredTy…
Browse files Browse the repository at this point in the history
…pe (OData#2565)

* Consider the OperationSegment when determining the TargetStructuredType

* Use IEdmType.GetHashCode() since we're dealing with instance equality, and fix handling of 'OperationSegment'

* Fix AspNetCore issue in previous commit
  • Loading branch information
ificator committed Sep 16, 2021
1 parent a629800 commit 7e03b23
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 16 deletions.
9 changes: 7 additions & 2 deletions src/Microsoft.AspNet.OData.Shared/Formatter/ClrTypeCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,13 @@ public bool Equals(EdmTypeCacheItem x, EdmTypeCacheItem y)

public int GetHashCode(EdmTypeCacheItem obj)
{
string combined = $"{obj.EdmType.FullTypeName()}~{obj.Nullable}";
return combined.GetHashCode();
unchecked
{
int hashCode = 17;
hashCode = (hashCode * 31) + obj.EdmType.GetHashCode();
hashCode = (hashCode * 31) + obj.Nullable.GetHashCode();
return hashCode;
}
}
}
}
Expand Down
40 changes: 27 additions & 13 deletions src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -629,20 +629,24 @@ public static IEdmType GetElementType(IEdmTypeReference edmTypeReference)
return edmTypeReference.Definition;
}

public static void GetPropertyAndStructuredTypeFromPath(IEnumerable<ODataPathSegment> segments,
out IEdmProperty property, out IEdmStructuredType structuredType, out string name)
public static void GetPropertyAndStructuredTypeFromPath(
IEnumerable<ODataPathSegment> segments,
out IEdmProperty property,
out IEdmStructuredType structuredType,
out string name)
{
property = null;
structuredType = null;
name = String.Empty;
string typeCast = String.Empty;
name = string.Empty;

if (segments != null)
{
string typeCast = string.Empty;

IEnumerable<ODataPathSegment> reverseSegments = segments.Reverse();
foreach (var segment in reverseSegments)
foreach (ODataPathSegment segment in reverseSegments)
{
NavigationPropertySegment navigationPathSegment = segment as NavigationPropertySegment;
if (navigationPathSegment != null)
if (segment is NavigationPropertySegment navigationPathSegment)
{
property = navigationPathSegment.NavigationProperty;
if (structuredType == null)
Expand All @@ -654,31 +658,41 @@ public static void GetPropertyAndStructuredTypeFromPath(IEnumerable<ODataPathSeg
return;
}

PropertySegment propertyAccessPathSegment = segment as PropertySegment;
if (propertyAccessPathSegment != null)
if (segment is OperationSegment operationSegment)
{
if (structuredType == null)
{
structuredType = operationSegment.EdmType as IEdmStructuredType;
}

name = operationSegment.Operations.First().FullName() + typeCast;
return;
}

if (segment is PropertySegment propertyAccessPathSegment)
{
property = propertyAccessPathSegment.Property;
if (structuredType == null)
{
structuredType = GetElementType(property.Type) as IEdmStructuredType;
}

name = property.Name + typeCast;
return;
}

EntitySetSegment entitySetSegment = segment as EntitySetSegment;
if (entitySetSegment != null)
if (segment is EntitySetSegment entitySetSegment)
{
if (structuredType == null)
{
structuredType = entitySetSegment.EntitySet.EntityType();
}

name = entitySetSegment.EntitySet.Name + typeCast;
return;
}

TypeSegment typeSegment = segment as TypeSegment;
if (typeSegment != null)
if (segment is TypeSegment typeSegment)
{
structuredType = GetElementType(typeSegment.EdmType.ToEdmTypeReference(false)) as IEdmStructuredType;
typeCast = "/" + structuredType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,39 @@
// </copyright>
//------------------------------------------------------------------------------

#if NETCORE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Query;
using Microsoft.AspNet.OData.Test.Abstraction;
using Microsoft.AspNet.OData.Test.Common;
using Microsoft.OData.Edm;
using Xunit;
#else
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Query;
using Microsoft.AspNet.OData.Test.Abstraction;
using Microsoft.AspNet.OData.Test.Common;
using Microsoft.OData.Edm;
using Xunit;
#endif

namespace Microsoft.AspNet.OData.Test
{
Expand Down Expand Up @@ -452,13 +472,17 @@ public async Task EnableQuery_DoesNotBlockQueries_WhenEverythingIsAllowed(string

[Theory]
[MemberData(nameof(AutoExpandedTestData))]
public async Task EnableQuery_Works_WithAutoExpanded(string queryString)
public async Task EnableQuery_Works_WithAutoExpand(string queryString)
{
// Arrange
string url = "http://localhost/odata/AutoExpandedCustomers";
Type[] controllers = new Type[] { typeof(AutoExpandedCustomersController) };

ODataModelBuilder builder = ODataConventionModelBuilderFactory.Create();
builder.EntitySet<AutoExpandedCustomer>("AutoExpandedCustomers");
builder.EntitySet<EnableQueryCategory>("EnableQueryCategories");
builder.EntityType<PremiumEnableQueryCategory>();

IEdmModel model = builder.GetEdmModel();
var server = TestServerFactory.Create(controllers, (config) =>
{
Expand All @@ -478,6 +502,59 @@ public async Task EnableQuery_Works_WithAutoExpanded(string queryString)
Assert.Contains("5678", responseString);
}

[Theory]
[InlineData(false, false)]
[InlineData(false, true)]
[InlineData(true, false)]
[InlineData(true, true)]
public async Task EnableQuery_Works_WithAutoExpandAndOperation(bool useAction, bool includeQueryString)
{
// Arrange
string baseUrl = "http://localhost/odata/AutoExpandedCustomers/";
Type[] controllers = new Type[] { typeof(AutoExpandedCustomersController) };

ODataModelBuilder builder = ODataConventionModelBuilderFactory.Create();
builder.EntitySet<AutoExpandedCustomer>("AutoExpandedCustomers");
builder.EntitySet<EnableQueryCategory>("EnableQueryCategories");
builder.EntityType<PremiumEnableQueryCategory>();

builder.EntityType<AutoExpandedCustomer>().Collection.Action("GetCategoryViaAction")
.ReturnsFromEntitySet<EnableQueryCategory>("EnableQueryCategories")
.Parameter(typeof(int), "id");
builder.EntityType<AutoExpandedCustomer>().Collection.Function("GetCategoryViaFunction")
.ReturnsFromEntitySet<EnableQueryCategory>("EnableQueryCategories")
.Parameter(typeof(int), "id");

IEdmModel model = builder.GetEdmModel();
var server = TestServerFactory.Create(controllers, (config) =>
{
config.MapODataServiceRoute("odata", "odata", model);
config.Count().OrderBy().Filter().Expand().MaxTop(null).Select();
});

HttpClient client = TestServerFactory.CreateClient(server);

// Act
HttpResponseMessage response = null;
if (useAction)
{
response = await client.PostAsync(
baseUrl + "GetCategoryViaAction" + (includeQueryString ? "?a=b" : ""),
new StringContent("{\"id\":1}", Encoding.UTF8, "application/json"));
}
else
{
response = await client.GetAsync(baseUrl + "GetCategoryViaFunction(id=1)" + (includeQueryString ? "?a=b" : ""));
}

string responseString = await response.Content.ReadAsStringAsync();

// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("1234", responseString);
Assert.Contains("5678", responseString);
}

[Theory]
[MemberData(nameof(UnsupportedDateTimeFunctionsTestData))]
public async Task EnableQuery_ReturnsBadRequest_ForUnsupportedFunctions(string queryString, string expectedElement)
Expand Down Expand Up @@ -608,6 +685,19 @@ public IQueryable<AutoExpandedCustomer> Get()
return _autoCustomers;
}

[EnableQuery]
[HttpPost]
public IQueryable<EnableQueryCategory> GetCategoryViaAction(ODataActionParameters parameters)
{
return _autoCustomers.Where(x => x.Id == (int)parameters["id"]).Select(x => x.Category).AsQueryable();
}

[EnableQuery]
public IQueryable<EnableQueryCategory> GetCategoryViaFunction(int id)
{
return _autoCustomers.Where(x => x.Id == id).Select(x => x.Category).AsQueryable();
}

static AutoExpandedCustomersController()
{
_autoCustomers = CreateAutoExpandedCustomers().AsQueryable();
Expand Down

0 comments on commit 7e03b23

Please sign in to comment.