Skip to content

Commit 7e03b23

Browse files
authored
Consider the OperationSegment when determining the TargetStructuredType (#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
1 parent a629800 commit 7e03b23

3 files changed

Lines changed: 125 additions & 16 deletions

File tree

src/Microsoft.AspNet.OData.Shared/Formatter/ClrTypeCache.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,13 @@ public bool Equals(EdmTypeCacheItem x, EdmTypeCacheItem y)
7171

7272
public int GetHashCode(EdmTypeCacheItem obj)
7373
{
74-
string combined = $"{obj.EdmType.FullTypeName()}~{obj.Nullable}";
75-
return combined.GetHashCode();
74+
unchecked
75+
{
76+
int hashCode = 17;
77+
hashCode = (hashCode * 31) + obj.EdmType.GetHashCode();
78+
hashCode = (hashCode * 31) + obj.Nullable.GetHashCode();
79+
return hashCode;
80+
}
7681
}
7782
}
7883
}

src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -629,20 +629,24 @@ public static IEdmType GetElementType(IEdmTypeReference edmTypeReference)
629629
return edmTypeReference.Definition;
630630
}
631631

632-
public static void GetPropertyAndStructuredTypeFromPath(IEnumerable<ODataPathSegment> segments,
633-
out IEdmProperty property, out IEdmStructuredType structuredType, out string name)
632+
public static void GetPropertyAndStructuredTypeFromPath(
633+
IEnumerable<ODataPathSegment> segments,
634+
out IEdmProperty property,
635+
out IEdmStructuredType structuredType,
636+
out string name)
634637
{
635638
property = null;
636639
structuredType = null;
637-
name = String.Empty;
638-
string typeCast = String.Empty;
640+
name = string.Empty;
641+
639642
if (segments != null)
640643
{
644+
string typeCast = string.Empty;
645+
641646
IEnumerable<ODataPathSegment> reverseSegments = segments.Reverse();
642-
foreach (var segment in reverseSegments)
647+
foreach (ODataPathSegment segment in reverseSegments)
643648
{
644-
NavigationPropertySegment navigationPathSegment = segment as NavigationPropertySegment;
645-
if (navigationPathSegment != null)
649+
if (segment is NavigationPropertySegment navigationPathSegment)
646650
{
647651
property = navigationPathSegment.NavigationProperty;
648652
if (structuredType == null)
@@ -654,31 +658,41 @@ public static void GetPropertyAndStructuredTypeFromPath(IEnumerable<ODataPathSeg
654658
return;
655659
}
656660

657-
PropertySegment propertyAccessPathSegment = segment as PropertySegment;
658-
if (propertyAccessPathSegment != null)
661+
if (segment is OperationSegment operationSegment)
662+
{
663+
if (structuredType == null)
664+
{
665+
structuredType = operationSegment.EdmType as IEdmStructuredType;
666+
}
667+
668+
name = operationSegment.Operations.First().FullName() + typeCast;
669+
return;
670+
}
671+
672+
if (segment is PropertySegment propertyAccessPathSegment)
659673
{
660674
property = propertyAccessPathSegment.Property;
661675
if (structuredType == null)
662676
{
663677
structuredType = GetElementType(property.Type) as IEdmStructuredType;
664678
}
679+
665680
name = property.Name + typeCast;
666681
return;
667682
}
668683

669-
EntitySetSegment entitySetSegment = segment as EntitySetSegment;
670-
if (entitySetSegment != null)
684+
if (segment is EntitySetSegment entitySetSegment)
671685
{
672686
if (structuredType == null)
673687
{
674688
structuredType = entitySetSegment.EntitySet.EntityType();
675689
}
690+
676691
name = entitySetSegment.EntitySet.Name + typeCast;
677692
return;
678693
}
679694

680-
TypeSegment typeSegment = segment as TypeSegment;
681-
if (typeSegment != null)
695+
if (segment is TypeSegment typeSegment)
682696
{
683697
structuredType = GetElementType(typeSegment.EdmType.ToEdmTypeReference(false)) as IEdmStructuredType;
684698
typeCast = "/" + structuredType;

test/UnitTest/Microsoft.AspNet.OData.Test.Shared/EnableQueryTests.cs

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,39 @@
55
// </copyright>
66
//------------------------------------------------------------------------------
77

8+
#if NETCORE
9+
using System;
10+
using System.Collections.Generic;
11+
using System.Linq;
12+
using System.Net;
13+
using System.Net.Http;
14+
using System.Text;
15+
using System.Threading.Tasks;
16+
using Microsoft.AspNetCore.Mvc;
17+
using Microsoft.AspNet.OData.Builder;
18+
using Microsoft.AspNet.OData.Extensions;
19+
using Microsoft.AspNet.OData.Query;
20+
using Microsoft.AspNet.OData.Test.Abstraction;
21+
using Microsoft.AspNet.OData.Test.Common;
22+
using Microsoft.OData.Edm;
23+
using Xunit;
24+
#else
825
using System;
926
using System.Collections.Generic;
1027
using System.Linq;
1128
using System.Net;
1229
using System.Net.Http;
30+
using System.Text;
1331
using System.Threading.Tasks;
32+
using System.Web.Http;
1433
using Microsoft.AspNet.OData.Builder;
1534
using Microsoft.AspNet.OData.Extensions;
1635
using Microsoft.AspNet.OData.Query;
1736
using Microsoft.AspNet.OData.Test.Abstraction;
1837
using Microsoft.AspNet.OData.Test.Common;
1938
using Microsoft.OData.Edm;
2039
using Xunit;
40+
#endif
2141

2242
namespace Microsoft.AspNet.OData.Test
2343
{
@@ -452,13 +472,17 @@ public async Task EnableQuery_DoesNotBlockQueries_WhenEverythingIsAllowed(string
452472

453473
[Theory]
454474
[MemberData(nameof(AutoExpandedTestData))]
455-
public async Task EnableQuery_Works_WithAutoExpanded(string queryString)
475+
public async Task EnableQuery_Works_WithAutoExpand(string queryString)
456476
{
457477
// Arrange
458478
string url = "http://localhost/odata/AutoExpandedCustomers";
459479
Type[] controllers = new Type[] { typeof(AutoExpandedCustomersController) };
480+
460481
ODataModelBuilder builder = ODataConventionModelBuilderFactory.Create();
461482
builder.EntitySet<AutoExpandedCustomer>("AutoExpandedCustomers");
483+
builder.EntitySet<EnableQueryCategory>("EnableQueryCategories");
484+
builder.EntityType<PremiumEnableQueryCategory>();
485+
462486
IEdmModel model = builder.GetEdmModel();
463487
var server = TestServerFactory.Create(controllers, (config) =>
464488
{
@@ -478,6 +502,59 @@ public async Task EnableQuery_Works_WithAutoExpanded(string queryString)
478502
Assert.Contains("5678", responseString);
479503
}
480504

505+
[Theory]
506+
[InlineData(false, false)]
507+
[InlineData(false, true)]
508+
[InlineData(true, false)]
509+
[InlineData(true, true)]
510+
public async Task EnableQuery_Works_WithAutoExpandAndOperation(bool useAction, bool includeQueryString)
511+
{
512+
// Arrange
513+
string baseUrl = "http://localhost/odata/AutoExpandedCustomers/";
514+
Type[] controllers = new Type[] { typeof(AutoExpandedCustomersController) };
515+
516+
ODataModelBuilder builder = ODataConventionModelBuilderFactory.Create();
517+
builder.EntitySet<AutoExpandedCustomer>("AutoExpandedCustomers");
518+
builder.EntitySet<EnableQueryCategory>("EnableQueryCategories");
519+
builder.EntityType<PremiumEnableQueryCategory>();
520+
521+
builder.EntityType<AutoExpandedCustomer>().Collection.Action("GetCategoryViaAction")
522+
.ReturnsFromEntitySet<EnableQueryCategory>("EnableQueryCategories")
523+
.Parameter(typeof(int), "id");
524+
builder.EntityType<AutoExpandedCustomer>().Collection.Function("GetCategoryViaFunction")
525+
.ReturnsFromEntitySet<EnableQueryCategory>("EnableQueryCategories")
526+
.Parameter(typeof(int), "id");
527+
528+
IEdmModel model = builder.GetEdmModel();
529+
var server = TestServerFactory.Create(controllers, (config) =>
530+
{
531+
config.MapODataServiceRoute("odata", "odata", model);
532+
config.Count().OrderBy().Filter().Expand().MaxTop(null).Select();
533+
});
534+
535+
HttpClient client = TestServerFactory.CreateClient(server);
536+
537+
// Act
538+
HttpResponseMessage response = null;
539+
if (useAction)
540+
{
541+
response = await client.PostAsync(
542+
baseUrl + "GetCategoryViaAction" + (includeQueryString ? "?a=b" : ""),
543+
new StringContent("{\"id\":1}", Encoding.UTF8, "application/json"));
544+
}
545+
else
546+
{
547+
response = await client.GetAsync(baseUrl + "GetCategoryViaFunction(id=1)" + (includeQueryString ? "?a=b" : ""));
548+
}
549+
550+
string responseString = await response.Content.ReadAsStringAsync();
551+
552+
// Assert
553+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
554+
Assert.Contains("1234", responseString);
555+
Assert.Contains("5678", responseString);
556+
}
557+
481558
[Theory]
482559
[MemberData(nameof(UnsupportedDateTimeFunctionsTestData))]
483560
public async Task EnableQuery_ReturnsBadRequest_ForUnsupportedFunctions(string queryString, string expectedElement)
@@ -608,6 +685,19 @@ public IQueryable<AutoExpandedCustomer> Get()
608685
return _autoCustomers;
609686
}
610687

688+
[EnableQuery]
689+
[HttpPost]
690+
public IQueryable<EnableQueryCategory> GetCategoryViaAction(ODataActionParameters parameters)
691+
{
692+
return _autoCustomers.Where(x => x.Id == (int)parameters["id"]).Select(x => x.Category).AsQueryable();
693+
}
694+
695+
[EnableQuery]
696+
public IQueryable<EnableQueryCategory> GetCategoryViaFunction(int id)
697+
{
698+
return _autoCustomers.Where(x => x.Id == id).Select(x => x.Category).AsQueryable();
699+
}
700+
611701
static AutoExpandedCustomersController()
612702
{
613703
_autoCustomers = CreateAutoExpandedCustomers().AsQueryable();

0 commit comments

Comments
 (0)