1

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)
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Nehio
  • 31
  • 1
  • 5
  • You can use the same field to order in `ThenBy` for types that does not need `ThenBy`. Or for string fields when you don't need to specify desc/asc you can try just concatenate them (also I maybe missing here some edge cases) – Guru Stron Apr 15 '21 at 11:18
  • I mean i know i could do OrderBy(a => condition ..) and repeat the condition with the then by and second sort but i was hoping there would be something more ummm shorter – Nehio Apr 15 '21 at 11:33
  • What if you write an extension method public static IOrderedEnumerable SmartOrderBy(this IEnumerable) in this method you can dynamically join all kind of linq opderations you like. There is no need for linq to be a one liner. – Klamsi Apr 15 '21 at 11:41
  • @Nehio, What is wrong with last one? – Svyatoslav Danyliv Apr 15 '21 at 12:26
  • @SvyatoslavDanyliv syntax not working and if you put () it doesnt work either. – Nehio Apr 15 '21 at 13:39
  • @Klamsi I'm working directly on an IQueryable that needs to be able to be translated into SQL. Ultimately i chose to write it using ThenBy() and repeating the condition with different column sort so i could achieve what i wanted. – Nehio Apr 15 '21 at 13:42

3 Answers3

2

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(....);

Klamsi
  • 846
  • 5
  • 16
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
Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • Your answer would be what i'd do but it's on an IQueryable on EF Core 5 so nope :) But nice solution ! – Nehio Apr 15 '21 at 13:38
1

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.

Nehio
  • 31
  • 1
  • 5