1

InvalidOperationException: The LINQ expression 'DbSet() .OrderBy(s => s.GetType().GetProperty(__sort_by_0).GetValue(s))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

This error occurres when I try to sort my rest api by property.enter image description here

  • Break the code up into smaller pieces to find issue. There is no advantage to writing code in one statement when the optimizer will do it for you. Having one statement makes it impossible to locate issues and fix. – jdweng Mar 14 '21 at 11:43
  • GetType() cannot be converted to sql – Crowcoder Mar 14 '21 at 12:04
  • Does this answer your question? [Dynamic LINQ OrderBy on IEnumerable / IQueryable](https://stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet-iqueryablet) – Crowcoder Mar 14 '21 at 12:16

1 Answers1

2

Someone linked a previous answer in a comment, which makes me unsure if I should be answering this, but well, it was already written. :)

Basically sorting dynamically is a bit more involved than what you're trying to do. That GetType(), GetProperty(), etc. reflection code cannot be executed in the database.

You have basically two options:

1) Use the Dynamic Linq package

With System.Linq.Dynamic.Core package (see documentation) you can provide string references to the members of entities, and it will be translated to expressions.

Which means you can simply write this:

_context.SwiftTransfers.OrderBy(sort_by).ToListAsync();

2) Use a custom solution to translate strings to expressions

You can build an expression yourself. For example the following method supports applying multiple ascending or descending ordering to an IQueryable, based on the provided string member name (which can reference nested properties with the pattern "User/Name/FirstName"):

static IQueryable<T> ApplyOrdering(IQueryable<T> query, string propertyPath, bool isAscending = true, bool firstOrdering = true)
{
    var param = Expression.Parameter(typeof(T), "p");
    var member = (MemberExpression)propertyPath.Split('/').Aggregate((Expression)param, Expression.Property);
    var exp = Expression.Lambda(member, param);
    string methodName = isAscending switch
    {
        true => firstOrdering ? "OrderBy" : "ThenBy",
        false => firstOrdering ? "OrderByDescending" : "ThenByDescending"
    };
    Type[] types = new Type[] { query.ElementType, exp.Body.Type };
    var orderByExpression = Expression.Call(typeof(Queryable), methodName, types, query.Expression, exp);
    return query.Provider.CreateQuery<T>(orderByExpression);
}

// Then call it like this:
ApplyOrdering(_context.SwiftTransfers.AsQueryable(), sort_by).ToListAsync();

This can be used as an extension method on IQueryable to access it more comfortably.

Tips

  • If you want to implement a standard-compliant way of sorting/filtering, you can look into OData. It has ready-to-use packages you can apply on your endpoints in ASP.NET Core. I personally don't really like their implementation suggestion, though, so I usually implement OData-compliant filtering and sorting manually.

  • If you use DTOs that change the shape of the data, and the client will send you sort parameters based on the DTO, you can run into issues where the given property name doesn't exist on the actual entity that you're trying to sort. But if you use AutoMapper for mapping, and if you map via ProjectTo<TDto>(), that will automatically translate the expressions back so that they reference the correct entity property. So you don't have to do manual property name mapping.

Leaky
  • 1,703
  • 2
  • 16
  • 24