OData / AspNetCoreOData

ASP.NET Core OData: A server library built upon ODataLib and ASP.NET Core
Other
457 stars 158 forks source link

$orderby fails on derived type property #1122

Open ArbnorZeqiri opened 11 months ago

ArbnorZeqiri commented 11 months ago

Assemblies affected ASP.NET Core OData 8.2.3

Describe the bug $orderby fails on derived type property

Reproduce steps Submit a request to obtain a navigation property, ordered by a property of a derived type.

Data Model

    public class Company
    {
        public int Id { get; set; }

        public ICollection<Customer> Customers { get; set; }
    }
    public class Customer
    {
        public int Id { get; set; }
    }

    public class PremiumCustomer : Customer
    {
        public int Nr { get; set; }
    }

EDM (CSDL) Model

<?xml version="1.0" encoding="utf-8"?>
  <edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
    <edmx:DataServices>
      <Schema Namespace="DataApi.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
        <EntityType Name="Customer">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Name="Id" Type="Edm.Int32" Nullable="false" />
        </EntityType>
        <EntityType Name="Company">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Name="Id" Type="Edm.Int32" Nullable="false" />
          <NavigationProperty Name="Customers" Type="Collection(DataApi.Models.Customer)" />
        </EntityType>
        <EntityType Name="PremiumCustomer" BaseType="DataApi.Models.Customer">
          <Property Name="Nr" Type="Edm.Int32" Nullable="false" />
        </EntityType>
      </Schema>
      <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
        <EntityContainer Name="Container">
          <EntitySet Name="Customers" EntityType="DataApi.Models.Customer" />
          <EntitySet Name="Companies" EntityType="DataApi.Models.Company">
            <NavigationPropertyBinding Path="Customers" Target="Customers" />
          </EntitySet>
        </EntityContainer>
      </Schema>
    </edmx:DataServices>
  </edmx:Edmx>

Request/Response ~Companies(3)/Customers?$orderby=DataApi.Models.PremiumCustomer/Nr desc

    {
        "error": {
            "code": "",
            "message": "The query specified in the URI is not valid. Syntax error at position 39 in 'DataApi.Models.PremiumCustomer/Nr desc;'.",
            "details": [],
            "innererror": {
            "message": "Syntax error at position 39 in 'DataApi.Models.PremiumCustomer/Nr desc;'.",
            "type": "Microsoft.OData.ODataException",
            "stacktrace": "   at Microsoft.OData.UriParser.ExpressionLexer.ValidateToken(ExpressionTokenKind t)\r\n   at Microsoft.OData.UriParser.UriQueryExpressionParser.ParseOrderBy(String orderBy)\r\n   at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseOrderByImplementation(String orderBy, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo)\r\n   at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseOrderBy()\r\n   at Microsoft.AspNetCore.OData.Query.OrderByQueryOption.get_OrderByClause()\r\n   at Microsoft.AspNetCore.OData.Query.Validator.OrderByQueryValidator.Validate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings)\r\n   at Microsoft.AspNetCore.OData.Query.Validator.ODataQueryValidator.Validate(ODataQueryOptions options, ODataValidationSettings validationSettings)\r\n   at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.ValidateQuery(HttpRequest request, ODataQueryOptions queryOptions)\r\n   at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.OnActionExecuting(ActionExecutingContext actionExecutingContext)"
            }
        }
    }

Expected behavior Result is ordered by PremiumCustomer.Nr

Additional context I see the same issue as in: https://github.com/OData/WebApi/issues/861, but I don't see it is fixed on AspNetCoreOData.

I tried ~Companies(3)/Customers/DataApi.Models.PremiumCustomer?$orderby=Nr desc, but got 404.

gathogojr commented 9 months ago

@ArbnorZeqiri Thank you for reporting the issue.

This ~Companies(3)/Customers?$orderby=DataApi.Models.PremiumCustomer/Nr desc wouldn't work because the Customers collection is of type Customer and we can't assume that every customer is a PremiumCustomer.

This ~Companies(3)/Customers/DataApi.Models.PremiumCustomer?$orderby=Nr desc should work but you need to implement an executable endpoint to handle that route.

If you're using conventional routing, implement a controller action in CompaniesController and name if GetCustomersFromPremiumCustomer. The controller action should accept an integer parameter named key:

public class CompaniesController : ODataController
{
    // ...
    public ActionResult<IEnumerable<PremiumCustomer>> GetCustomersFromPremiumCustomer(int key)
    {
        // ...
    }
    // ...
}

If you're using attribute routing, or if you should find that the route is not supported conventionally, you can apply the template to the HttpGet attribute:

public class CompaniesController : ODataController
{
    // ...
    [HttpGet("Companies({key})/Customers/DataApi.Models.PremiumCustomer")]
    public ActionResult<IEnumerable<PremiumCustomer>> GetPremiumCustomers(int key)
    {
        // ...
    }
    // ...
}

I hope that helps.

gathogojr commented 9 months ago

The guidance I have provided above is the same provided in the issue you linked https://github.com/OData/WebApi/issues/861#issuecomment-319837468

tomfrenzel commented 2 months ago

I just updated one of my projects to .NET 8 and the issue seems to be gone. But I'm not sure whether it was fixed on the AspNetCore OData side or on the EF Core side, since I just updated Microsoft.AspNetCore.OData from 8.2.3 to 8.2.5 and didn't see anything corresponding to this issue in the release notes. EF Core on the other side was 7.0.14 previously.

Those are the package versions I am currently using and where the issue doesn't occur anymore:


<PackageReference Include="Microsoft.AspNetCore.OData.NewtonsoftJson" Version="8.2.0" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.1" />`