(You are currently looking at the first edition version of this page. This page is also available for the second
and third editions.)

Notes for Chapter 11: Query expressions and LINQ to Objects

11.1.1: What does LINQ mean?

Eric gave his own answer to my question of what "counts" as LINQ (as well as emphasizing that it really doesn't matter):

[...] There is something real and new here. That real and new thing is that the semantics of the query are expressible in the language you are programming in. In the old days of building a SQL string, the semantics of the query aren't in the C# code, they're in the string. The compiler has no way of knowing whether the thing in the string is sensible or not. To me, LINQ is any technology that moves the query logic more into the C#/VB language and out of string manipulation or object model calls.

11.1.2: Deferred execution and iterator blocks

One area which apparently causes lots of people pain is understanding that iterator blocks really do only start to get used when you iterate over the results. That was true when we looked at iterators in chapter 6, and it's equally true if you use a custom iterator as a data source (or custom operator) in LINQ.

Possibly the biggest source of confusion is around parameter validation - if the method does parameter validation, you might instinctively expect it to throw an exception as soon as you call the method, even if you understand that data is only going to be processed when you iterate over the result. This is certainly desirable, and I think it's a pity the language doesn't (currently) support this scenario. I've blogged
about this in more detail.

11.1.2: Truly silly query expressions

It's important to understand that the process of translating
a query expression into method calls is purely syntactic - that's how
it's able to work in radically different ways with different providers.

I have a blog entry demonstrating just how silly things can get - you can make query expressions call static methods or delegates, if you provide the right (somewhat strange) data source.

If you build a LINQ provider which makes Select return an int, so be it. You shouldn't expect anyone to actually use it, but the compiler won't care at all.

11.1.3: Naming of imaginary companies and products

The fictional company was originally called Skeetysoft, but Eric reckons that "PascalCasing is the coolest casing". Who am I to argue, beyond pointing out that he works for Microsoft rather than MicroSoft? ;)

Additionally, I'm somewhat inconsistent in my choice of naming for products. Currently SkeetySoft is competing with Microsoft (SkeetyOffice/SkeetyMediaPlayer) and Google (SkeetyTalk). I should either have been consistently challenging Microsoft by calling my chat application SkeetyMessenger, or perhaps decided to throw down the gauntlet to Apple as well, with SkeetyTunes as the media app.
Good job this is all in my mind, otherwise I'm sure the relevant CEOs would all be quaking in their boots.

11.1.3: Defect severities

My choice of Trivial/Minor/Major/Showstopper as the available severities was somewhat arbitrary, but it does have the nice feature of restricting choice quite a lot, leaving clear options. I know that restricting choice sounds like a bad thing, but often too much choice can lead to time being wasted on decisions which make no real difference. Take the Java logging API for example - how often am I really going to care whether a log level is at "fine" or "finer"?

11.3.3: Explicitly specifying an ascending order

One feature I missed when writing about orderings is the ability to explicitly use the word ascending for a particular ordering. For example,

from person in people
order by person.Nameselect person

is exactly equivalent to

from person in people
order by person.Name ascendingselect person

The reason I missed this? I've never seen it used, that I recall. (Yes, I should still have spotted it when going through the spec, of course...)

11.5.1: Equijoins

You may have been slightly surprised to see the word equals in join expressions, rather than ==, but there are reasons for it:

If == were allowed, it would give the impression that other operators (<=, != etc) were allowed too.

It enforces the notion that this is an equijoin, not just an arbitrary expression.

The scoping rules are different for the two sides - one range variable is available on the left side, a different one is available on the right side. It would be very odd to have what appeared to be a normal expression, but which had different values available on different sides.

11.5.1: Scoping of key selectors in joins

I mention the "scoping issue" briefly after listing 11.12, but it's worth going into this slightly more fully. Let's consider joining two ranges of numbers together in the obvious way. This query expression is valid:

from left in Enumerable.Range(0, 10)join right in Enumerable.Range(5, 15)on left equals rightselect left*right

This, however, is not:

// Warning: invalid!from left in Enumerable.Range(0, 10)join right in Enumerable.Range(5, 15)on right equals leftselect left*right

The left side of the equals only knows about the "main" sequence, whereas the right side only knows about the extra sequence which is being joined with the main one. We can see why it fails when the compiler translation is performed on each of the two expressions above. First the working one:

Neither of the key selector lambda expressions will compile, because they refer to variables which aren't available.

11.6.1: Copy editors get notes too

Just a quick nod to my copy editor, Liz, who did such a wonderful job with the book. She mentioned after listing 11.17 that entirely coincidentally, she used to work with a Tara, a Tim and a Darren (three of the SkeetySoft employees).

Given the trivial nature of some of the notes I've been adding, I see no reason not to include this little factoid. It's nice to get an opportunity to thank her again, too. If I ever write another book, I really want to have Liz as my copy editor again.