Skip to content

Sorting by an interface member from a lambda expression fails #1161

Closed
@ngeien

Description

@ngeien

DESCRIPTION

Sorting by an interface member from a lambda expression fails in v5.0.1. It was working before in v4.2.0.
It is beneficial to implement common sorting logic in a generic resource definition.

STEPS TO REPRODUCE

The following example code is based on your GettingStarted example. A generic resource definition that supports sorting for all models that implement an ISortable interface.

public interface ISortable
{
    [Attr]
    int SortOrder { get; set; }
}

[Resource]
public sealed class Book : Identifiable<int>, ISortable
{
    [Attr]
    public string Title { get; set; } = null!;

    [Attr]
    public int PublishYear { get; set; }

    [HasOne]
    public Person Author { get; set; } = null!;

    [Attr]
    public int SortOrder { get; set; }

}
public class SortableResourceDefinition<TResource> : JsonApiResourceDefinition<TResource, int>
    where TResource : class, IIdentifiable<int>, ISortable
{
    public SortableResourceDefinition(IResourceGraph resourceGraph)
        : base(resourceGraph)
    { }

    public override SortExpression? OnApplySort(SortExpression? existingSort)
    {
        return existingSort == null
            ? CreateSortExpressionFromLambda(new PropertySortOrder { (resource => resource.SortOrder, ListSortDirection.Ascending) })
            : existingSort;
    }
}

public class BookDefinition : SortableResourceDefinition<Book>
{
    public BookDefinition(IResourceGraph resourceGraph)
        : base(resourceGraph)
    { }
}
  1. Add the ISortable interface and update the model Book
  2. Add the generic resource definition and its implementation for the book resource.
  3. Add the resource definition service at startup.

EXPECTED BEHAVIOR

It should return an ordered collection by the property SortOrder.

ACTUAL BEHAVIOR

It raises an exception in SortExpressionLambdaConverter.cs:

{
    "id": "5a2eb44b-35d7-4e5c-9299-25e5ad1cf1ef",
    "status": "500",
    "title": "Invalid lambda expression for sorting from resource definition. It should select a property that is exposed as an attribute, or a to-many relationship followed by Count(). The property can be preceded by a path of to-one relationships. Examples: 'blog => blog.Title', 'blog => blog.Posts.Count', 'blog => blog.Author.Name.LastName'.",
    "detail": "The lambda expression 'resource => Convert(resource.SortOrder, Object)' is invalid. Type 'GettingStarted.Models.ISortable' does not exist in the resource graph."
}

Possible solution

internal sealed class SortExpressionLambdaConverter
{
   private Expression? ReadAttribute(Expression expression)
   {
       ...
-      ResourceType resourceType = memberExpression.Member.Name == nameof(Identifiable<object>.Id) && memberExpression.Expression != null
-                ? _resourceGraph.GetResourceType(memberExpression.Expression.Type)
-                : _resourceGraph.GetResourceType(memberExpression.Member.DeclaringType!);
+      ResourceType resourceType = memberExpression.Expression != null
+                ? _resourceGraph.GetResourceType(memberExpression.Expression.Type)
+                : _resourceGraph.GetResourceType(memberExpression.Member.DeclaringType!);
       ...
   }
}

VERSIONS USED

  • JsonApiDotNetCore version: 5.0.1
  • ASP.NET Core version: 6.0.6
  • Entity Framework Core version: 6.0.6
  • Database provider: Sqlite

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions