Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion sample/ODataRoutingSample/ODataRoutingSample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.6" />
<PackageReference Include="Microsoft.OData.Edm" Version="8.2.3" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0" />
<PackageReference Include="Microsoft.OpenApi.OData" Version="1.6.9" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
Expand Down
27 changes: 27 additions & 0 deletions src/Microsoft.AspNetCore.OData/Edm/EdmHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.ModelBuilder.Annotations;
using Microsoft.OData.ModelBuilder.Config;
using Microsoft.OData.UriParser;

namespace Microsoft.AspNetCore.OData.Edm;

Expand Down Expand Up @@ -428,6 +429,32 @@ public static ModelBoundQuerySettings GetModelBoundQuerySettings(this IEdmModel
}
}

/// <summary>
/// Gets the EDM type reference from the query node.
/// </summary>
/// <param name="edmModel">The Edm type.</param>
/// <param name="queryNode">Query node to extract the EDM type reference from.</param>
/// <param name="errorMessageParameter">The error message parameter. If present, it will be included in the error message when <paramref name="queryNode"/> is not supported. Defaults to null.</param>
/// <returns></returns>
public static IEdmTypeReference GetEdmTypeReferenceFromQueryNode(this IEdmModel edmModel, QueryNode queryNode, string errorMessageParameter = null)
{
IEdmTypeReference targetEdmTypeReference = null;
if (queryNode is ConstantNode constantNode)
{
targetEdmTypeReference = edmModel.FindType((string)constantNode.Value).ToEdmTypeReference(false);
}
else if (queryNode is SingleResourceCastNode singleResourceCastNode)
{
targetEdmTypeReference = singleResourceCastNode.TypeReference;
}
else if (errorMessageParameter != null)
{
throw Error.NotSupported(SRResources.QueryNodeBindingNotSupported, queryNode.Kind, errorMessageParameter);
}

return targetEdmTypeReference;
}

private static ModelBoundQuerySettings GetModelBoundQuerySettings<T>(this IEdmModel edmModel, T key, DefaultQueryConfigurations defaultQueryConfigs = null)
where T : IEdmElement
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.OData.ModelBuilder" Version="2.0.0" />
<PackageReference Include="Microsoft.OData.Core" Version="8.2.3" />
<PackageReference Include="Microsoft.OData.Edm" Version="8.2.3" />
<PackageReference Include="Microsoft.Spatial" Version="8.2.3" />
<PackageReference Include="Microsoft.OData.Core" Version="8.4.0" />
<PackageReference Include="Microsoft.OData.Edm" Version="8.4.0" />
<PackageReference Include="Microsoft.Spatial" Version="8.4.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
9 changes: 9 additions & 0 deletions src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2264,6 +2264,15 @@
<param name="isNullable">Nullable value.</param>
<returns>The Edm type reference.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Edm.EdmHelpers.GetEdmTypeReferenceFromQueryNode(Microsoft.OData.Edm.IEdmModel,Microsoft.OData.UriParser.QueryNode,System.String)">
<summary>
Gets the EDM type reference from the query node.
</summary>
<param name="edmModel">The Edm type.</param>
<param name="queryNode">Query node to extract the EDM type reference from.</param>
<param name="errorMessageParameter">The error message parameter. If present, it will be included in the error message when <paramref name="queryNode"/> is not supported. Defaults to null.</param>
<returns></returns>
</member>
<member name="T:Microsoft.AspNetCore.OData.Edm.EdmModelAnnotationExtensions">
<summary>
The extensions for the <see cref="T:Microsoft.OData.Edm.IEdmModel"/> for the annotations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,14 +307,11 @@ private Expression BindIsOf(SingleValueFunctionCallNode node)
return FalseConstant;
}

string typeName = (string)((ConstantNode)node.Parameters.Last()).Value;

IEdmType edmType = Model.FindType(typeName);
IEdmTypeReference edmTypeReference = Model.GetEdmTypeReferenceFromQueryNode(node.Parameters.Last());

Type clrType = null;
if (edmType != null)
if (edmTypeReference != null)
{
// bool nullable = source.Type.IsNullable();
IEdmTypeReference edmTypeReference = edmType.ToEdmTypeReference(false);
clrType = Model.GetClrType(edmTypeReference);
}

Expand Down Expand Up @@ -715,13 +712,12 @@ private Expression BindCastSingleValue(SingleValueFunctionCallNode node)
Contract.Assert(arguments.Length == 1 || arguments.Length == 2);

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

if (targetEdmType != null)
IEdmTypeReference targetEdmTypeReference = Model.GetEdmTypeReferenceFromQueryNode(node.Parameters.Last());

Type targetClrType = null;
if (targetEdmTypeReference != null)
{
IEdmTypeReference targetEdmTypeReference = targetEdmType.ToEdmTypeReference(false);
targetClrType = Model.GetClrType(targetEdmTypeReference);

if (source != NullConstant)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -567,13 +567,13 @@ protected virtual Expression BindCastSingleValue(SingleValueFunctionCallNode nod
Contract.Assert(arguments.Length == 1 || arguments.Length == 2);

Expression source = arguments.Length == 1 ? context.CurrentParameter : arguments[0];
string targetTypeName = (string)((ConstantNode)node.Parameters.Last()).Value;
IEdmType targetEdmType = context.Model.FindType(targetTypeName);

IEdmTypeReference targetEdmTypeReference = context.Model.GetEdmTypeReferenceFromQueryNode(node.Parameters.Last());

Type targetClrType = null;

if (targetEdmType != null)
if (targetEdmTypeReference != null)
{
IEdmTypeReference targetEdmTypeReference = targetEdmType.ToEdmTypeReference(false);
targetClrType = context.Model.GetClrType(targetEdmTypeReference);

if (source != NullConstant)
Expand Down Expand Up @@ -650,14 +650,11 @@ protected virtual Expression BindIsOf(SingleValueFunctionCallNode node, QueryBin
return FalseConstant;
}

string typeName = (string)((ConstantNode)node.Parameters.Last()).Value;

IEdmType edmType = context.Model.FindType(typeName);
IEdmTypeReference edmTypeReference = context.Model.GetEdmTypeReferenceFromQueryNode(node.Parameters.Last());

Type clrType = null;
if (edmType != null)
if (edmTypeReference != null)
{
// bool nullable = source.Type.IsNullable();
IEdmTypeReference edmTypeReference = edmType.ToEdmTypeReference(false);
clrType = context.Model.GetClrType(edmTypeReference);
}

Expand Down
19 changes: 2 additions & 17 deletions src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -664,27 +664,12 @@ public virtual Expression BindSingleResourceCastFunctionCall(SingleResourceFunct

IEdmModel model = context.Model;

string targetEdmTypeName = null;
QueryNode queryNode = node.Parameters.Last();
if (queryNode is ConstantNode constantNode)
{
targetEdmTypeName = constantNode.Value as string;
}
else if (queryNode is SingleResourceCastNode singleResourceCastNode)
{
targetEdmTypeName = singleResourceCastNode.TypeReference.FullName();
}
else
{
throw Error.NotSupported(SRResources.QueryNodeBindingNotSupported, queryNode.Kind, "BindSingleResourceCastFunctionCall");
}
IEdmTypeReference targetEdmType = context.Model.GetEdmTypeReferenceFromQueryNode(node.Parameters.Last(), nameof(BindSingleResourceCastFunctionCall));

IEdmType targetEdmType = model.FindType(targetEdmTypeName);
Type targetClrType = null;

if (targetEdmType != null)
{
targetClrType = model.GetClrType(targetEdmType.ToEdmTypeReference(false));
targetClrType = model.GetClrType(targetEdmType);
}

if (arguments[0].Type == targetClrType)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//-----------------------------------------------------------------------------
// <copyright file="IsOfAndCastController.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Routing.Controllers;

namespace Microsoft.AspNetCore.OData.E2E.Tests.IsOfAndCast;

public class IsOfAndCastController : ODataController
{
private static IsOfAndCastDataSource _dataSource = new IsOfAndCastDataSource();

[EnableQuery]
[HttpGet("odata/products")]
public IActionResult GetProducts()
{
return Ok(_dataSource.Products);
}

[EnableQuery]
[HttpGet("odata/orders")]
public IActionResult GetOrders()
{
return Ok(_dataSource.Orders);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//-----------------------------------------------------------------------------
// <copyright file="IsOfAndCastDataModel.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Microsoft.AspNetCore.OData.E2E.Tests.IsOfAndCast;

public class Product
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public Domain Domain { get; set; }
public Double Weight { get; set; }
}

[Flags]
public enum Domain
{
Military = 1,
Civil = 2,
Both = 3,
}

public class AirPlane : Product
{
public int Speed { get; set; }
public string Model { get; set; }
}

public class JetPlane : AirPlane
{
public string JetType { get; set; }
}

public class Order
{
[Key]
public int OrderID { get; set; }
public Address Location { get; set; }
public IList<Product> Products { get; set; }
}

public class Address
{
public string City { get; set; }
}

public class HomeAddress : Address
{
public string HomeNo { get; set; }
}

public class OfficeAddress : Address
{
public string OfficeNo { get; set; }
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//-----------------------------------------------------------------------------
// <copyright file="IsOfAndCastDataSource.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using System.Collections.Generic;

namespace Microsoft.AspNetCore.OData.E2E.Tests.IsOfAndCast;

public class IsOfAndCastDataSource
{
public IsOfAndCastDataSource()
{
ResetData();
InitializeData();
}

private void ResetData()
{
this.Products?.Clear();
this.Orders?.Clear();
}

public IList<Product> Products { get; private set; }
public IList<Order> Orders { get; private set; }

private void InitializeData()
{
this.Products = new List<Product>
{
new Product
{
ID = 1,
Name = "Product1",
Domain = Domain.Civil,
Weight = 1000
},
new Product
{
ID = 2,
Name = "Product2",
Domain = Domain.Military,
Weight = 2000,
},
new AirPlane
{
ID = 3,
Name = "Product3",
Domain = Domain.Both,
Weight = 1500,
Speed = 900,
Model = "Boeing 737"
},
new JetPlane
{
ID = 4,
Name = "Product4",
Domain = Domain.Civil,
Weight = 1200,
Speed = 1000,
Model = "Airbus A320",
JetType = "Turbofan"
},
new JetPlane
{
ID = 5,
Name = "Product5",
Domain = Domain.Military,
Weight = 1800,
Speed = 1500,
Model = "F-22 Raptor",
JetType = "Afterburning Turbofan"
}
};

this.Orders = new List<Order>
{
new Order
{
OrderID = 1,
Location = new Address { City = "City1" },
Products = new List<Product> { this.Products[0], this.Products[2] }
},
new Order
{
OrderID = 2,
Location = new HomeAddress { City = "City2", HomeNo = "100NO" },
Products = new List<Product> { this.Products[1], this.Products[3], this.Products[4] }
},
new Order
{
OrderID = 3,
Location = new OfficeAddress { City = "City3", OfficeNo = "300NO" },
Products = new List<Product> { this.Products[0], this.Products[2], this.Products[3] }
},
new Order
{
OrderID = 4,
Location = new HomeAddress { City = "City4", HomeNo = "200NO" },
Products = new List<Product> { this.Products[1], this.Products[4] }
}
};
}
}
Loading