'LINQ OrderBy using condition AND Multiple columns

These two work

query.OrderBy(a => a.Name).ThenBy(a => a.LastName)

Also works

query.OrderBy( a=> a.Type == 1 ? a.Name : otherTypeSortOrderColumn)

Similarly you can do

from a in query orderby a.Name,a.LastName select a;

and

From a in query orderby (a.type == 1 ? a.Name : otherTypeSortOrderColumn) select a

How do you mix both?

For each value of Type I want a different column sort, on which there might or might not be more columns that has to have the "ThenBy" sorting applied

Something like

query.OrderBy ( a => a.Type == 1 ? a.Name, A.LastName : a.Type == 2 ? a.Product.Name : ... and so on)


Solution 1:[1]

Maybe you are not aware that you can join linq operations as ever you like

if (thisAndThat)
    orderedQuery = query.OrderBy(...);

if (anotherCondition)
    orderedQuery = orderedQuery.ThenBy(....);

Solution 2:[2]

If you are working AsEnumerable, consider to create an IComparer for it.

class PropertyComparer<T, TProperty> : IComparer<T>
{
    public Func<T, TProperty> PropertySelector {get; set;}
    public IComparer<TProperty> PropertyComparer {get; set;} = Comparer<TProperty>.Default;

    public Compare(T x, T y)
    {
        TProperty propertyX = this.PropertySelector(x);
        TProperty propertyY = this.PropertySelector(y);
        return this.PropertyComparer.Compare(propertyX, propertyY);
    }
}

Usage:

var customerComparer = new PropertyComparer<Customer, string>
{
    PropertySelector = customer => customer.Name,
    PropertyComparer = StringComparer.CurrentCultureIgnoreCase,
}

IEnumerable<Customer> customers = ...
var customersOrderedByName = customers.OrderBy(customer => customer, customerComparer);

There's room for improvement

What if a Customer.Name equals null? Change IComparer.Compare:

public Compare(T x, T y)
{
    // TODO: decide if NULL comes first or last
    if (x == null)
    {
        if (y == null)
            return 0; // both null
        else
            return +1; // null comes last
    }
    else if (y ==  null)
    {
        return -1;
    }
    else
    {
        // x and y both not null

Solution 3:[3]

I have not found a way to achieve this yet so for now i'm going with a bit more verbose approach to this :

Expression<Func<TEntity, object>> keySelector = x =>
                            !x.a.HasValue && x.b.HasValue && !x.c.HasValue && !string.IsNullOrEmpty(x.c.LastName) ? x.c.LastName :
                            !x.a.HasValue && !x.b.HasValue && !x.c.HasValue && x.d.HasValue ? x.d.Name :
                            !x.a.HasValue && !x.b.HasValue && !x.c.HasValue && !x.d.HasValue && x.e.HasValue ? x.e.Name :
                            !x.a.HasValue && !x.b.HasValue && !x.c.HasValue && !x.d.HasValue && !x.e.HasValue && x.f.HasValue && !string.IsNullOrEmpty(x.f.Society) ? x.f.Society : string.Empty;

Expression<Func<TEntity, object>> keySelector2 = x =>
                                !x.a.HasValue && !x.b.HasValue && x.c.HasValue && !string.IsNullOrEmpty(x.c.FirstName) ? x.c.FirstName :
                                !x.a.HasValue && !x.b.HasValue && !x.c.HasValue && !x.d.HasValue && !x.e.HasValue && x.f.HasValue && !string.IsNullOrEmpty(x.f.LastName) ? x.f.LastName : string.Empty;



Expression<Func<TEntity, object>> keySelector3 = x =>
                        !x.a.HasValue && !x.b.HasValue && !x.c.HasValue && !x.d.HasValue && !x.e.HasValue && x.f.HasValue && !string.IsNullOrEmpty(x.f.FirstName) ? x.f.FirstName : string.Empty;

query = query.OrderBy(keySelector).ThenBy(keySelector2).ThenBy(keySelector3);

Where TEntity is the type of the class from IQueryable i'm accessing.

If someone has anything better i'm open to suggestions. I thought about implementing an extension using Linq.Extensions to fiddle around with something cleaner but since this is an edge case it would just be hiding more code behind a pretty front.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Klamsi
Solution 2
Solution 3