3
\$\begingroup\$

Here is my OrderBy method which includes concatenated column names. Can this be achieved more efficiently?

public static IQueryable<T> Sort<T>(this IQueryable<T> query, List<SortationDto> sortations)
    where T : BaseEntity
{
    sortations.ForEach(s =>
    {
        Expression value = null;
        ParameterExpression parameter = Expression.Parameter(query.ElementType, "item");

        // if sortation is a concatenation of two columns
        if (s.Concatenated.Count > 0)
        {
            Expression concat = null;
            s.Concatenated.ForEach(c =>
            {
                //get first property
                value = c.Column.Split('.').Aggregate((Expression)parameter, Expression.PropertyOrField);

                //if first property add as initial property
                if (concat == null)
                {
                    concat = value;
                }
                else //else concatenate with first property
                {
                    MethodInfo concatMethod = typeof(String).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) });
                    value = Expression.Call(concatMethod, concat, Expression.Constant(" "), value);
                }
            });
        }
        else //else use one and only property
        {
            value = s.Column.Split('.').Aggregate((Expression)parameter, Expression.PropertyOrField);
        }

        //build lambda expression
        LambdaExpression predicate = Expression.Lambda(value, parameter);

        //define orderby method name based on current iteration of sortations
        string methodName = (sortations.IndexOf(s) == 0 ? "Order" : "Then") + (s.Direction == "asc" ? "By" : "ByDescending");

        //call method
        MethodCallExpression orderBy = Expression.Call(typeof(Queryable), methodName, new Type[] { query.ElementType, value.Type }, query.Expression, Expression.Quote(predicate));

        //add final expression to query
        query = query.Provider.CreateQuery<T>(orderBy);
    });

    return query;
}
\$\endgroup\$
  • \$\begingroup\$ What's the point of sorting concatenations when you're already supporting sorting by multiple columns? \$\endgroup\$ – svick Feb 21 '16 at 15:26
  • \$\begingroup\$ Good point! I'll rework this method. \$\endgroup\$ – Sean Thorburn Feb 21 '16 at 16:12
1
\$\begingroup\$

Removed unnecessary concatenation.

public static IQueryable<T> Sort<T>(this IQueryable<T> query, List<SortationDto> sortations)
    where T : BaseEntity
{
    List<SortationDto> combined = sortations.Where(s => s.Concatenated.Count == 0).Union(sortations.SelectMany(so => so.Concatenated)).ToList();

    combined.ForEach(s =>
    {
        ParameterExpression parameter = Expression.Parameter(query.ElementType, "item");
        Expression value = s.Column.Split('.').Aggregate((Expression)parameter, Expression.PropertyOrField);
        LambdaExpression predicate = Expression.Lambda(value, parameter);

        string methodName = (combined.IndexOf(s) == 0 ? "Order" : "Then") + (s.Direction == "asc" ? "By" : "ByDescending");
        MethodCallExpression orderBy = Expression.Call(typeof(Queryable), methodName, new Type[] { query.ElementType, value.Type }, query.Expression, Expression.Quote(predicate));             
        query = query.Provider.CreateQuery<T>(orderBy);
    });

    return query;
}
\$\endgroup\$
0
\$\begingroup\$

Thanks for sharing your code! Works like a charm. Ended up using it with some adjustments and the extension method below. I pass the Sort objects in so that any parsing is done outside (separate the concern a bit).

public static IQueryable<TEntity> ApplySort<TEntity>(this IQueryable<TEntity> query, params Sort[] sorts)
    {
        if (sorts == null)
            return query;
        var applicableSorts = sorts.Where(s => s != null);
        if (!applicableSorts.Any())
            return query;
        applicableSorts
            .Select((item, index) => new { Index = index, item.PropertyName, item.Direction })
            .ToList()
            .ForEach(sort =>
            {
                ParameterExpression parameterExpression = Expression.Parameter(query.ElementType, "entity");
                var propertyExpression = Expression.Property(parameterExpression, sort.PropertyName);
                var sortPredicate = Expression.Lambda(propertyExpression, parameterExpression);

                string methodName = (sort.Index == 0 ? "Order" : "Then") + (sort.Direction == OrderDirection.Ascending ? "By" : "ByDescending");
                MethodCallExpression orderBy = Expression.Call(typeof(Queryable), methodName, new Type[] { query.ElementType, propertyExpression.Type }, query.Expression, Expression.Quote(sortPredicate));
                query = query.Provider.CreateQuery<TEntity>(orderBy);
            });

        return query;
    }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.