Skip to content

Commit 3d50b02

Browse files
authored
Support SingleResourceCastNode for unquoted type parameters in BindIsOf and BindCastSingleValue (#1511)
* Handle SingleResourceCastNode conversion * bump ODL version and fix failing tests
1 parent 7c9e1ba commit 3d50b02

File tree

13 files changed

+687
-113
lines changed

13 files changed

+687
-113
lines changed

sample/ODataRoutingSample/ODataRoutingSample.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
1212
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.6" />
1313
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.6" />
14-
<PackageReference Include="Microsoft.OData.Edm" Version="8.2.3" />
1514
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0" />
1615
<PackageReference Include="Microsoft.OpenApi.OData" Version="1.6.9" />
1716
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />

src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2929
</PackageReference>
3030
<PackageReference Include="Microsoft.OData.ModelBuilder" Version="2.0.0" />
31-
<PackageReference Include="Microsoft.OData.Core" Version="8.2.3" />
32-
<PackageReference Include="Microsoft.OData.Edm" Version="8.2.3" />
33-
<PackageReference Include="Microsoft.Spatial" Version="8.2.3" />
31+
<PackageReference Include="Microsoft.OData.Core" Version="8.4.0" />
32+
<PackageReference Include="Microsoft.OData.Edm" Version="8.4.0" />
33+
<PackageReference Include="Microsoft.Spatial" Version="8.4.0" />
3434
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0">
3535
<PrivateAssets>all</PrivateAssets>
3636
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

src/Microsoft.AspNetCore.OData/Query/Expressions/ExpressionBinderBase.cs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -307,14 +307,20 @@ private Expression BindIsOf(SingleValueFunctionCallNode node)
307307
return FalseConstant;
308308
}
309309

310-
string typeName = (string)((ConstantNode)node.Parameters.Last()).Value;
310+
IEdmTypeReference edmTypeReference = null;
311+
QueryNode queryNode = node.Parameters.Last();
312+
if (queryNode is ConstantNode constantNode)
313+
{
314+
edmTypeReference = Model.FindType((string)constantNode.Value)?.ToEdmTypeReference(false);
315+
}
316+
else if (queryNode is SingleResourceCastNode singleResourceCastNode)
317+
{
318+
edmTypeReference = singleResourceCastNode.TypeReference;
319+
}
311320

312-
IEdmType edmType = Model.FindType(typeName);
313321
Type clrType = null;
314-
if (edmType != null)
322+
if (edmTypeReference != null)
315323
{
316-
// bool nullable = source.Type.IsNullable();
317-
IEdmTypeReference edmTypeReference = edmType.ToEdmTypeReference(false);
318324
clrType = Model.GetClrType(edmTypeReference);
319325
}
320326

@@ -715,13 +721,21 @@ private Expression BindCastSingleValue(SingleValueFunctionCallNode node)
715721
Contract.Assert(arguments.Length == 1 || arguments.Length == 2);
716722

717723
Expression source = arguments.Length == 1 ? this.Parameter : arguments[0];
718-
string targetTypeName = (string)((ConstantNode)node.Parameters.Last()).Value;
719-
IEdmType targetEdmType = Model.FindType(targetTypeName);
720-
Type targetClrType = null;
721724

722-
if (targetEdmType != null)
725+
IEdmTypeReference targetEdmTypeReference = null;
726+
QueryNode queryNode = node.Parameters.Last();
727+
if (queryNode is ConstantNode constantNode)
728+
{
729+
targetEdmTypeReference = Model.FindType((string)constantNode.Value)?.ToEdmTypeReference(false);
730+
}
731+
else if (queryNode is SingleResourceCastNode singleResourceCastNode)
732+
{
733+
targetEdmTypeReference = singleResourceCastNode.TypeReference;
734+
}
735+
736+
Type targetClrType = null;
737+
if (targetEdmTypeReference != null)
723738
{
724-
IEdmTypeReference targetEdmTypeReference = targetEdmType.ToEdmTypeReference(false);
725739
targetClrType = Model.GetClrType(targetEdmTypeReference);
726740

727741
if (source != NullConstant)

src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.SingleValueFunctionCall.cs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -567,13 +567,22 @@ protected virtual Expression BindCastSingleValue(SingleValueFunctionCallNode nod
567567
Contract.Assert(arguments.Length == 1 || arguments.Length == 2);
568568

569569
Expression source = arguments.Length == 1 ? context.CurrentParameter : arguments[0];
570-
string targetTypeName = (string)((ConstantNode)node.Parameters.Last()).Value;
571-
IEdmType targetEdmType = context.Model.FindType(targetTypeName);
570+
571+
IEdmTypeReference targetEdmTypeReference = null;
572+
QueryNode queryNode = node.Parameters.Last();
573+
if (queryNode is ConstantNode constantNode)
574+
{
575+
targetEdmTypeReference = context.Model.FindType((string)constantNode.Value)?.ToEdmTypeReference(false);
576+
}
577+
else if (queryNode is SingleResourceCastNode singleResourceCastNode)
578+
{
579+
targetEdmTypeReference = singleResourceCastNode.TypeReference;
580+
}
581+
572582
Type targetClrType = null;
573583

574-
if (targetEdmType != null)
584+
if (targetEdmTypeReference != null)
575585
{
576-
IEdmTypeReference targetEdmTypeReference = targetEdmType.ToEdmTypeReference(false);
577586
targetClrType = context.Model.GetClrType(targetEdmTypeReference);
578587

579588
if (source != NullConstant)
@@ -650,14 +659,20 @@ protected virtual Expression BindIsOf(SingleValueFunctionCallNode node, QueryBin
650659
return FalseConstant;
651660
}
652661

653-
string typeName = (string)((ConstantNode)node.Parameters.Last()).Value;
662+
IEdmTypeReference edmTypeReference = null;
663+
QueryNode queryNode = node.Parameters.Last();
664+
if (queryNode is ConstantNode constantNode)
665+
{
666+
edmTypeReference = context.Model.FindType((string)constantNode.Value)?.ToEdmTypeReference(false);
667+
}
668+
else if (queryNode is SingleResourceCastNode singleResourceCastNode)
669+
{
670+
edmTypeReference = singleResourceCastNode.TypeReference;
671+
}
654672

655-
IEdmType edmType = context.Model.FindType(typeName);
656673
Type clrType = null;
657-
if (edmType != null)
674+
if (edmTypeReference != null)
658675
{
659-
// bool nullable = source.Type.IsNullable();
660-
IEdmTypeReference edmTypeReference = edmType.ToEdmTypeReference(false);
661676
clrType = context.Model.GetClrType(edmTypeReference);
662677
}
663678

src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -664,27 +664,25 @@ public virtual Expression BindSingleResourceCastFunctionCall(SingleResourceFunct
664664

665665
IEdmModel model = context.Model;
666666

667-
string targetEdmTypeName = null;
667+
IEdmTypeReference targetEdmType = null;
668668
QueryNode queryNode = node.Parameters.Last();
669669
if (queryNode is ConstantNode constantNode)
670670
{
671-
targetEdmTypeName = constantNode.Value as string;
671+
targetEdmType = model.FindType((string)constantNode.Value)?.ToEdmTypeReference(false);
672672
}
673673
else if (queryNode is SingleResourceCastNode singleResourceCastNode)
674674
{
675-
targetEdmTypeName = singleResourceCastNode.TypeReference.FullName();
675+
targetEdmType = singleResourceCastNode.TypeReference;
676676
}
677677
else
678678
{
679-
throw Error.NotSupported(SRResources.QueryNodeBindingNotSupported, queryNode.Kind, "BindSingleResourceCastFunctionCall");
679+
throw Error.NotSupported(SRResources.QueryNodeBindingNotSupported, queryNode.Kind, nameof(BindSingleResourceCastFunctionCall));
680680
}
681681

682-
IEdmType targetEdmType = model.FindType(targetEdmTypeName);
683682
Type targetClrType = null;
684-
685683
if (targetEdmType != null)
686684
{
687-
targetClrType = model.GetClrType(targetEdmType.ToEdmTypeReference(false));
685+
targetClrType = model.GetClrType(targetEdmType);
688686
}
689687

690688
if (arguments[0].Type == targetClrType)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//-----------------------------------------------------------------------------
2+
// <copyright file="IsOfAndCastController.cs" company=".NET Foundation">
3+
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
4+
// See License.txt in the project root for license information.
5+
// </copyright>
6+
//------------------------------------------------------------------------------
7+
8+
using Microsoft.AspNetCore.Mvc;
9+
using Microsoft.AspNetCore.OData.Query;
10+
using Microsoft.AspNetCore.OData.Routing.Controllers;
11+
12+
namespace Microsoft.AspNetCore.OData.E2E.Tests.IsOfAndCast;
13+
14+
public class IsOfAndCastController : ODataController
15+
{
16+
private static IsOfAndCastDataSource _dataSource = new IsOfAndCastDataSource();
17+
18+
[EnableQuery]
19+
[HttpGet("odata/products")]
20+
public IActionResult GetProducts()
21+
{
22+
return Ok(_dataSource.Products);
23+
}
24+
25+
[EnableQuery]
26+
[HttpGet("odata/orders")]
27+
public IActionResult GetOrders()
28+
{
29+
return Ok(_dataSource.Orders);
30+
}
31+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//-----------------------------------------------------------------------------
2+
// <copyright file="IsOfAndCastDataModel.cs" company=".NET Foundation">
3+
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
4+
// See License.txt in the project root for license information.
5+
// </copyright>
6+
//------------------------------------------------------------------------------
7+
8+
using System;
9+
using System.Collections.Generic;
10+
using System.ComponentModel.DataAnnotations;
11+
12+
namespace Microsoft.AspNetCore.OData.E2E.Tests.IsOfAndCast;
13+
14+
public class Product
15+
{
16+
[Key]
17+
public int ID { get; set; }
18+
public string Name { get; set; }
19+
public Domain Domain { get; set; }
20+
public Double Weight { get; set; }
21+
}
22+
23+
[Flags]
24+
public enum Domain
25+
{
26+
Military = 1,
27+
Civil = 2,
28+
Both = 3,
29+
}
30+
31+
public class AirPlane : Product
32+
{
33+
public int Speed { get; set; }
34+
public string Model { get; set; }
35+
}
36+
37+
public class JetPlane : AirPlane
38+
{
39+
public string JetType { get; set; }
40+
}
41+
42+
public class Order
43+
{
44+
[Key]
45+
public int OrderID { get; set; }
46+
public Address Location { get; set; }
47+
public IList<Product> Products { get; set; }
48+
}
49+
50+
public class Address
51+
{
52+
public string City { get; set; }
53+
}
54+
55+
public class HomeAddress : Address
56+
{
57+
public string HomeNo { get; set; }
58+
}
59+
60+
public class OfficeAddress : Address
61+
{
62+
public string OfficeNo { get; set; }
63+
}
64+
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//-----------------------------------------------------------------------------
2+
// <copyright file="IsOfAndCastDataSource.cs" company=".NET Foundation">
3+
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
4+
// See License.txt in the project root for license information.
5+
// </copyright>
6+
//------------------------------------------------------------------------------
7+
8+
using System.Collections.Generic;
9+
10+
namespace Microsoft.AspNetCore.OData.E2E.Tests.IsOfAndCast;
11+
12+
public class IsOfAndCastDataSource
13+
{
14+
public IsOfAndCastDataSource()
15+
{
16+
ResetData();
17+
InitializeData();
18+
}
19+
20+
private void ResetData()
21+
{
22+
this.Products?.Clear();
23+
this.Orders?.Clear();
24+
}
25+
26+
public IList<Product> Products { get; private set; }
27+
public IList<Order> Orders { get; private set; }
28+
29+
private void InitializeData()
30+
{
31+
this.Products = new List<Product>
32+
{
33+
new Product
34+
{
35+
ID = 1,
36+
Name = "Product1",
37+
Domain = Domain.Civil,
38+
Weight = 1000
39+
},
40+
new Product
41+
{
42+
ID = 2,
43+
Name = "Product2",
44+
Domain = Domain.Military,
45+
Weight = 2000,
46+
},
47+
new AirPlane
48+
{
49+
ID = 3,
50+
Name = "Product3",
51+
Domain = Domain.Both,
52+
Weight = 1500,
53+
Speed = 900,
54+
Model = "Boeing 737"
55+
},
56+
new JetPlane
57+
{
58+
ID = 4,
59+
Name = "Product4",
60+
Domain = Domain.Civil,
61+
Weight = 1200,
62+
Speed = 1000,
63+
Model = "Airbus A320",
64+
JetType = "Turbofan"
65+
},
66+
new JetPlane
67+
{
68+
ID = 5,
69+
Name = "Product5",
70+
Domain = Domain.Military,
71+
Weight = 1800,
72+
Speed = 1500,
73+
Model = "F-22 Raptor",
74+
JetType = "Afterburning Turbofan"
75+
}
76+
};
77+
78+
this.Orders = new List<Order>
79+
{
80+
new Order
81+
{
82+
OrderID = 1,
83+
Location = new Address { City = "City1" },
84+
Products = new List<Product> { this.Products[0], this.Products[2] }
85+
},
86+
new Order
87+
{
88+
OrderID = 2,
89+
Location = new HomeAddress { City = "City2", HomeNo = "100NO" },
90+
Products = new List<Product> { this.Products[1], this.Products[3], this.Products[4] }
91+
},
92+
new Order
93+
{
94+
OrderID = 3,
95+
Location = new OfficeAddress { City = "City3", OfficeNo = "300NO" },
96+
Products = new List<Product> { this.Products[0], this.Products[2], this.Products[3] }
97+
},
98+
new Order
99+
{
100+
OrderID = 4,
101+
Location = new HomeAddress { City = "City4", HomeNo = "200NO" },
102+
Products = new List<Product> { this.Products[1], this.Products[4] }
103+
}
104+
};
105+
}
106+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//-----------------------------------------------------------------------------
2+
// <copyright file="IsOfAndCastEdmModel.cs" company=".NET Foundation">
3+
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
4+
// See License.txt in the project root for license information.
5+
// </copyright>
6+
//------------------------------------------------------------------------------
7+
8+
using Microsoft.OData.Edm;
9+
using Microsoft.OData.ModelBuilder;
10+
11+
namespace Microsoft.AspNetCore.OData.E2E.Tests.IsOfAndCast;
12+
13+
public class IsOfAndCastEdmModel
14+
{
15+
public static IEdmModel GetEdmModel()
16+
{
17+
var builder = new ODataConventionModelBuilder();
18+
builder.EntitySet<Order>("Orders");
19+
builder.EntitySet<Product>("Products");
20+
var airPlaneType = builder.EntityType<AirPlane>();
21+
airPlaneType.DerivesFrom<Product>();
22+
23+
builder.Namespace = typeof(Product).Namespace;
24+
return builder.GetEdmModel();
25+
}
26+
}

0 commit comments

Comments
 (0)