Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ - How do I keep my (complex) results ordered?

I have LINQ query that is built up in a piecemeal fashion like so:

var initialQuery = from item in MyContext where xxx == yyy select item;    
var furtherQuery = from item in initialQuery where bla == foo select new { some stuff };

// more code here...

// eventually:
var yetAnotherQuery = (from item in furtherQuery ...)
                      .OrderBy(my_condition);

// As far as I know, the following query should still maintain the order of the previous one
// see: https://stackoverflow.com/questions/911942/are-subqueries-in-linqtosql-guaranteed-to-be-in-the-same-order-as-their-parent

var stillAnotherQuery = (from item in yetAnotherQuery
                         select item.data_I_care_about)
                         .Distinct();

// And finally...
var finalQuery = stillAnotherQuery.Skip(PageIndex).Take(PageSize);

But I am getting an exception when Skip() is called, saying that the query is not ordered! So apparently what is indicated in my code comment above and the referenced SO question is not entirely true. In fact another SO answer indicates that the preservation of that order is not guaranteed.

Does anyone know a good way to do what I am trying to accomplish?

I considered simply including a ROW_NUMBER in my intermediate results, and ordering by that at the very end, but I cannot find a way to get that ROW_NUMBER in my results via LINQ.

I have seen several other SO questions trying to get the ROW_NUMBER in there, but they are all clientside, as far as I have seen.

I seem to have painted myself in a corner. Anyone know a (LINQ-friendly) way out?


UPDATE


Some have suggested that I do the Distinct() before the OrderBy(). I believe that would give me different results.

Imagine I have this table of data

myRank | myData
-------+--------
   3   |    A
   1   |    B
   2   |    A

Supposing I am ordering by myRank, and the data I care about is myData, and imagine my original code was like this:

var query = from item in MyTable
            select item;

query = query.OrderBy(item => item.myRank);

var derivedQuery = from item in query        // Note: we throw away myRank
                   select item.myData;

derivedQuery = derivedQuery.Distinct();

If I swap the order of the OrderBy() and Distinct(), I will get different results. I do not want myRank to be included in the Distinct().

Sorry, this is part of a much larger process, so it is hard to get all the details into this question.

But hopefully that makes sense?

like image 351
jwd Avatar asked Sep 09 '11 21:09

jwd


People also ask

Does LINQ Group by keep order?

Found answer on MSDN: Yes. The IGrouping<TKey, TElement> objects are yielded in an order based on the order of the elements in source that produced the first key of each IGrouping<TKey, TElement> . Elements in a grouping are yielded in the order they appear in source.

Does LINQ join preserve order?

Note that the ordering is performed after the join. Although you can use an orderby clause with one or more of the source sequences before the join, generally we do not recommend it. Some LINQ providers might not preserve that ordering after the join.

Does group by preserve order?

Groupby preserves the order of rows within each group.

What does the .include method do in LINQ?

LINQ Include allows retrieving the related entities to be read from database in same query. By using the Include method we can easily read all related entities from the database in a single query.


1 Answers

The problem is not that the elements are no longer ordered.

Rather, the problem is that Distinct() returns a IQueryable<T> whereas OrderBy() returns IOrderedQueryable<TSource>, which is (apparently) required for the paging to work with EF

Swapping the Distinct() and OrderBy() steps should fix things

Edit

I suggest something roughly like this:

var query = from item in MyTable
        select item;

query = query.GroupBy(item => item.myData, item => item.myRank);
var derivedQuery = query.OrderBy(group => group.Min())
                        .Select(group.Key);

Clarifying:

  • group by will result in dinstinct keys (myData) anyway
  • sorting by the Min() of myRank per group will effect the same order as sorting (ascending) by myRank globally first, then doing a Distinc()
like image 158
sehe Avatar answered Oct 11 '22 23:10

sehe