Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The first thing to note is that</p> <pre><code>someList.OrderBy(item =&gt; item.SomeProp).OrderBy(item =&gt; item.SomeOtherProp); </code></pre> <p>Is more or less equivalent to:</p> <pre><code>someList.OrderBy(item =&gt; item.SomeOtherProp); </code></pre> <p>Because the second <code>OrderBy</code> undoes the work of the first. Generally you want:</p> <pre><code>someList.OrderBy(item =&gt; item.SomeProp).ThenBy(item =&gt; item.SomeOtherProp); </code></pre> <p>Note that the equivalent:</p> <pre><code>from item in someList orderby item.SomeProp, item.SomeOtherProp select item </code></pre> <p>Makes use of <code>ThenBy</code> as above.</p> <p>Now, with either syntax, with Linq-to-objects (but not database and other linq query providers) <code>OrderBy</code> works through a call to <code>IComparable&lt;T&gt;</code> if available, and <code>IComparable</code> otherwise (barring an exception we'll come to later). Since <code>agreementsMatching</code> is a list in memory, this is the form used. This is how <code>OrderBy</code> can know what "order by" means for a given type.</p> <p>Strings, and all the built-in numeric types (<code>int</code>, <code>double</code>, etc), implement <code>IComparable&lt;T&gt;</code> so they can all be used fine without any action from you.</p> <p>Presumably at least one of the properties you were ordering by above wasn't one of these types, but a type of your own. I can't tell which from your code, so I'm going to make up the following:</p> <p>I'm going to assume that the <code>Specification</code> property returned a <code>Spec</code> object, and that <code>Spec</code> objects should be ordered by their <code>Name</code> property in a case-insensitive manner according to the invariant culture. So I'm starting with:</p> <pre><code>class Spec { public property Name { get { /* code I don't care about here*/ } set { /* code I don't care about here*/ } } /* more code I don't care about here*/ } </code></pre> <p>I add an implementation of <code>IComparable&lt;Spec&gt;</code>. It's also a good idea to implement <code>IComparable</code> in such cases, for backwards compatibility, though it could be skipped. Both define a method that compares the instance with another object, returning a number &lt; 0 if the instance is "lesser" (comes first in order), 0 if they are equivalent in ordering and >0 if the instance is "greater":</p> <pre><code>class Spec : IComparable&lt;Spec&gt;, IComparable { public property Name { get { /* code I don't care about here*/ } set { /* code I don't care about here*/ } } /* more code I don't care about here*/ public int CompareTo(Spec other) { if(other == null) return 1; //Often we make use of an already-existing comparison, though not always return string.Compare(Name, other.Name, StringComparison.InvariantCultureIgnoreCase) } //For backwards compatibility: public int CompareTo(object other) { if(other == null) return 1; Spec os = other as Spec; if(os == null) throw new ArgumentException("Comparison between Spec and " + other.GetType().FullName + " is not allowed"); return CompareTo(os); } } </code></pre> <p>Now, <code>OrderBy</code> can handle comparisons of <code>Spec</code> objects, which it will do by name (also, we can use <code>List&lt;Spec&gt;.Sort()</code> and a whole bunch of other things.</p> <p>A final matter, is what happens if we need to sort by some other rules, or if we need to sort a type where we don't have the source code and it doesn't implement <code>IComparable&lt;T&gt;</code> or <code>IComparable</code>?</p> <p>Here we can create a class that implements <code>IComparer&lt;T&gt;</code>, and this will bypass the use of <code>IComparable&lt;T&gt;</code>. Here's an example that demonstrates this:</p> <pre><code>public class OddBeforeEven : IComparer&lt;int&gt; { public int Compare(int x, int y) { int compareOddEven = y % 2 - x % 2; if(compareOddEven != 0) return compareOddEven; //if both odd or both even, use default ordering: return x.CompareTo(y); } } /* ... */ var oddBeforeEven0To20 = Enumerable.Range(0, 21).OrderBy(x =&gt; x, new OddBeforeEven()); /*Enumerating oddBeforeEven0To20 will produce 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20*/ </code></pre> <p>A final note.</p> <p>Are you sure you need to <code>ToList()</code> in your question?</p> <p>Newcomers to Linq often call <code>ToList()</code> a lot partly to keep things in a structure they can picture better, and partly because a lot of tutorial examples will make heavy use of it.</p> <p>There are certainly times when <code>ToList()</code> is the only sensible thing to do, and also times when it is necessary to bring something from a non-in-memory form of Linq (such as against a database or XMLDocument) to in-memory. However:</p> <p>If something starts in the database, then most of the time it's best to keep it there as long as possible. There are plenty of exceptions to this, but generally look to keep things in the database and optimise by bringing it into memory as an optimisation for those few exceptions, rather than being in the habit of bringing things into memory quickly and then optimising for the 97% of the time when keeping it in the database is faster!</p> <p>If you need to switch to linq-to-objects, <code>ToEnumerable()</code> will do this without eagerly executing the query, so it's better than <code>ToList()</code> most of the time.</p> <p>There are times when <code>ToList()</code> is the most efficient (especially if you have to hit the same list twice), but you should be calling it when you can see the clear need for it, not by default.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload