score:26

Accepted answer

without using an extension method....

create a custom icomparer<string> to check the empty values before using the default string.compare. the first checks will return -1 instead of 1 or 1 instead of -1, if using the standard string comparison.

/// <summary>
/// returns -1 instead of 1 if y is isnullorempty when x is not.
/// </summary>
public class emptystringsarelast : icomparer<string>
{
    public int compare(string x, string y)
        {
            if (string.isnullorempty(y) && !string.isnullorempty(x))
            {
                return -1;
            }
            else if (!string.isnullorempty(y) && string.isnullorempty(x))
            {
                return 1;
            }
            else
            {
                return string.compare(x, y);
            }
        }
 }

pass your emptystringsarelast comparer into the orderby of lambda expression. in this solution teams who have entered the race should appear alphabetical order, but the unaffiliated race entries should appear at then end.

var entries = repository.race.where(e => e.eventid == id)
                          .orderby(e => e.teamname, new emptystringsarelast())
                          .thenby(e => e.lastname)
                          .thenby(e => e.firstname);

score:0

it works for me:

    private static iqueryable<t> getorderquery<t>(this iqueryable<t> q, basefiltercollection filter)
    {
        q = q.orderby(getexpression<t>(filter.sortfield));

        var param = expression.parameter(typeof(t), "p");
        var prop = expression.property(param, filter.sortfield);
        var exp = expression.lambda(prop, param);
        string method = filter.sortdirection == sortdirectiontype.asc ? "thenby" : "thenbydescending";
        type[] types = { q.elementtype, exp.body.type };
        var rs = expression.call(typeof(queryable), method, types, q.expression, exp);
        return q.provider.createquery<t>(rs);
    }

    private static expression<func<t, bool>> getexpression<t>(string sortfield)
    {
        parameterexpression param = expression.parameter(typeof(t), "p");
        expression prop = expression.property(param, sortfield);

        var info = typeof(t).getproperty(sortfield, bindingflags.ignorecase | bindingflags.public | bindingflags.instance);
        expression exp = expression.equal(prop, info.propertytype.isvaluetype 
            ? expression.constant(activator.createinstance(info.propertytype)) 
            : expression.constant(null));

        return expression.lambda<func<t, bool>>(exp, param);
    }

score:0

you dont need to complicate, the easiest way is to do something like this:

yourlist.orderbydescending(x => string.isnullorempty(x.value)

use orderbydescending or orderby depending on if you want to see empty strings in the beginning or last.

regards

score:1

building on dave anson's answer, you can user comparer.create() to create the comparer from a lambda. here's an example that sorts unsorted by its mystring string fields, with null or empty strings appearing last.

var sorted = unsorted.orderby(x => x.mystring, comparer<string>.create((x, y) => { 
             if ( string.isnullorempty(y) && !string.isnullorempty(x)) return -1;
        else if (!string.isnullorempty(y) &&  string.isnullorempty(x)) return +1;
        else return string.compare(x, y);
    }))

(to put them first, switch the signs on the 1 constants)

score:3

this answer is perhaps what you were originally looking for - using your generic extension method:

    public static iqueryable<t> orderbyfieldnullslast<t>(this iqueryable<t> q, string sortfield, bool ascending)
    {
        //we are rebuilding .orderbydescending(p => p.sortfield.hasvalue).thenby(p => p.sortfield)
        //i.e. sort first by whether sortfield has a value, then by sortfield asc or sortfield desc

        //create the expression tree that represents the generic parameter to the predicate
        var param = expression.parameter(typeof(t), "p");

        //create an expression tree that represents the expression p=>p.sortfield.hasvalue 
        var prop = expression.property(param, sortfield);
        var hasvalue = expression.property(prop, "hasvalue");
        var exp = expression.lambda(hasvalue, param);

        string method = "orderbydescending";
        type[] types = new type[] { q.elementtype, exp.body.type };
        var orderbycallexpression = expression.call(typeof(queryable), method, types, q.expression, exp);

        //now do the thenby bit,sending in the above expression to the expression.call
        exp = expression.lambda(prop, param);
        types = new type[] { q.elementtype, exp.body.type };
        method = ascending ? "thenby" : "thenbydescending";
        var thenbycallexpression = expression.call(typeof(queryable), method, types,orderbycallexpression, exp);


        return q.provider.createquery<t>(thenbycallexpression);
    }

score:49

the simplest way is to use

orderby(e => string.isnullorempty(e.teamname)

this doesn't require any extension method or custom icomparer implementation etc.

var entries = repository.race.where(e => e.eventid == id)
                      .orderby(e => string.isnullorempty(e.teamname))
                      .thenby(e => e.lastname)
                      .thenby(e => e.firstname);

Related Query

More Query from same tag