bradwestness / collate-dot-net

Filtering, sorting and paging extensions for .NET IQueryables
MIT License
21 stars 4 forks source link

Sort throws System.ArgumentNullException: Value cannot be null. (Parameter 'member') #10

Closed Alexn closed 10 months ago

Alexn commented 10 months ago

I need to apply sorting by field with chained name, such as "Node.Name".

Use case:

var query = dataContext
                    .Set<TreeItem>()
                    .GroupJoin(dataContext.Set<ImageInfo>(),
                                    x => x.ImageId,
                                    x => x.Id,
                                    (node, images) => new { node, img = images })
                    .SelectMany(x => x.img.DefaultIfEmpty(),
                                     (x, img) => new NodeWithImageRecord(x.node, x.img.FirstOrDefault()));

var records = await query
                                 .Sort("Node.Name")
                                 .ToArrayAsync(cancellationToken);

Error:

System.ArgumentNullException: Value cannot be null. (Parameter 'member') at System.Dynamic.Utils.ContractUtils.RequiresNotNull(Object value, String paramName) at System.Linq.Expressions.Expression.MakeMemberAccess(Expression expression, MemberInfo member) at Collate.Internal.SortExpressionBuilder.ApplySorts[T](IOrderedQueryable1& source, IEnumerable1 sorts)

My implementation of QrderBy with chained name support:

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query,
                                                  string propertyName,
                                                  bool ascending = true)
    {
        if (propertyName == null)
            throw new ArgumentNullException(nameof(propertyName));

        ParameterExpression parameter = Expression.Parameter(typeof(T), "x");

        Expression body = parameter;
        foreach (string member in propertyName.Split('.'))
            body = Expression.PropertyOrField(body, member);

        dynamic lambda = Expression.Lambda(body, parameter);

        return ascending
                   ? Queryable.OrderBy(query, lambda)
                   : Queryable.OrderByDescending(query, lambda);
    }
bradwestness commented 10 months ago

Would you mind including the definition of your TreeItem and ImageInfo types here? It seems like this is a use-case for the .NavigationSort() method.

Alexn commented 10 months ago

Hi, I found NavigationSort, but the NavigationSort supports only one chain, In onther case I need to order by "Node.Group.SortOrder".

bradwestness commented 10 months ago

I just released a new version (1.8.0) which adds support for automatically doing NavigationSorts when the field name has dotted navigation in it. Good suggestion!