tags:

views:

165

answers:

4

Is there anyway this code can be refactored? The only difference is the order by part.

Idealy I'd like to use a delegate/lamda expression so the code is reusable but I don't know how to conditionally add and remove the query operators OrderBy and OrderByDescending

var linq = new NorthwindDataContext();

        var query1 = linq.Customers
            .Where(c => c.ContactName.StartsWith("a"))
            .SelectMany(cus=>cus.Orders)
            .OrderBy(ord => ord.OrderDate)
            .Select(ord => ord.CustomerID);

        var query2 = linq.Customers
            .Where(c => c.ContactName.StartsWith("a"))
            .SelectMany(cus => cus.Orders)
            .OrderByDescending(ord => ord.OrderDate)
            .Select(ord => ord.CustomerID);
A: 

With numbers, etc you can normally just negate the 'ordering variable'.

With DateTime, I am not so sure. You could try using a Timespan.

leppie
+1  A: 

You can split your query up into bits, and use control flow logic. LINQ to SQL will magically construct the correct query as if you had typed it all one line! The reason this works is that the query is not sent to the database until you request the data, but instead is stored as an expression.

var linq = new NorthwindDataContext();
var query = linq.Customers
    .Where(c => c.ContactName.StartsWith("a"))
    .SelectMany(cus=>cus.Orders);

IOrderedQueryable<Order> query2;
if (useAscending) {
    query2 = query.OrderBy(ord => ord.OrderDate);
} else {
    query2 = query.OrderByDescending(ord => ord.OrderDate);
}

var query3 = query2.Select(ord => ord.CustomerID);
Mark Byers
Errorrr!!!!!!!!! Your ordering needs to happen before select, else you dealing with a different type :)
leppie
Thanks Mark. Does it matter if the order by query operators are placed after the select() ?
Sir Psycho
@leppie: Yeah, sorry I didn't notice that!
Mark Byers
A: 

Well, If you have a condition where you decide if the order by is ascending or descending you can use this

var query1 = linq.Customers
.Where(c => c.ContactName.StartsWith("a"))
.SelectMany(cus=>cus.Orders)

if(SortAscending)
   query1 = query1.OrderBy(ord => ord.OrderDate);
else
   query1 = query1.OrderByDescending(ord => ord.OrderDate);

var query2 = query1.Select(ord => ord.CustomerID);
Nikos Steiakakis
See, you made the same mistake as the answer below (it is now fixed).
leppie
@leppie: Not only is it fixed, but it is above now, not below. So your comment is inaccurate and probably more confusing than helpful! Why not just repeat your comment rather than trying to use a reference? In other words, just write this: 'Your ordering needs to happen before select, else you dealing with a different type'. ;-)
Mark Byers
There, fixed it. However I think I prefer jon's answer my self as well ;-)
Nikos Steiakakis
@Mark Byers: It is not inaccurate, as you have a different sort order. Mine is default (newest first), so your comment is inaccurate! :)
leppie
@leppie: Oh, I forgot that the sort order is configurable. I just always have sorted by votes... OK, you win :-P
Mark Byers
+3  A: 

You can create your own reusable extension method which will do this:

public IOrderedQueryable<TSource> OrderBy<TSource, TKey>
    (this IQueryable<TSource> source,
     Expression<Func<TSource, TKey>> keySelector,
     boolean ascending)
{
     return ascending ? source.OrderBy(keySelector)
          : source.OrderByDescending(keySelector);
}

and similarly for ThenBy:

public IOrderedQueryable<TSource> ThenBy<TSource, TKey>
    (this IOrderedQueryable<TSource> source,
     Expression<Func<TSource, TKey>> keySelector,
     boolean ascending)
{
     return ascending ? source.ThenBy(keySelector)
          : source.ThenByDescending(keySelector);
}
Jon Skeet
Cool. Thanks Jon.
Sir Psycho
Kinda off topic, but will the above translate correctly to Linq2SQL? IOW is it clever enough to see an 'unsupported' method and execute it before it builds a syntax tree and generate the SQL? Just wondering, never tried it. :)
leppie
@leppie: It's just calling into the existing Queryable methods - those are what build up the expression tree. Note that this *won't* work against `IEnumerable<T>` at the moment, although you could easily write equivalent extension methods which would.
Jon Skeet
@Jon Skeet: Right. I have not really dived into that before. So it executes the 'entire' query to receive a 'syntax tree' to build SQL. Makes sense, thanks :)
leppie