LINQ: Notation, Syntax, and Snags

Welcome to the final post in my four part series on LINQ. So far, we've talked about:

For our last look into LINQ (at least for this mini-series), I want to tackle the mini-war of "dot notation" versus "query syntax", and look at some of the pitfalls that can be avoided by using LINQ responsibly.

Let Battle Commence…

For anyone who has written LINQ using C# (or VB.NET), you are probably aware that there is more than one way to express the query (two of which, sane people might use):

  1. Old school static method calls
  2. Method syntax
  3. Query syntax

No one in their right mind should be using the first of these options; extension methods were invented to alleviate the pain that would be caused by writing LINQ this way1. Extension methods, static methods that can be called as though member methods, are the reason why we have the second option of method syntax (more commonly known as dot notation or fluent notation). The final option, query syntax, is also known as "syntactical sugar", some language keywords that can make coding easier. These keywords map to concepts found in LINQ methods and query syntax is what gives LINQ it's name; Language INtegrated Query2.

They all map to the same thing, a sequence of methods that can be executed, or translated into an expression tree, evaluated by a LINQ provider, and executed. Anything written in one of these approaches can be written using the others. There is often contention on whether to use dot notation or query syntax, as if one is inherently better than the other, but as we all know, only the Sith deal in absolutes3.  Hopefully, by the end of these examples you will see how each has its merits.

Why are LINQ queries not always called like regular methods?

Because sometimes, such as in LINQ-to-SQL or LINQ-to-Entity Framework, the method calls need to be translated into SQL or some other querying syntax, allowing queries to take advantage of server-side querying optimizations. For a more in-depth look at all things LINQ, including the way the language keywords map to the method calls, I recommend looking at Jon Skeet's Edulinq series, which is available as a handy e-book.

Before we begin, here is a quick summary of the C# keywords that we have for writing queries in query syntax: from, group, orderby, let, join, where and select.  There are also contextual keywords to be used in conjunction with one or two of the main keywords:in, into, ascending, descending, by, on and equals. Each of these keywords has a corresponding equivalent method or methods in LINQ although it can sometimes be a little more complicated as we shall see.

So, let us look at an example and see how it can be expressed using dot notation and query syntax4). For an example, let us look at a simple projection of people to their last names.

These two queries do the exact same thing, but I find that the dot notation wins out because it takes less typing and it looks clearer. However, if we decide we want to only get the ones that were born before 1980, things look a little more even.

Here, there is not much difference between them, so I'd probably leave this to personal preference5. However, as soon as we want a distinct list, the dot notation starts to win out again because C# does not contain a distinct keyword (though VB.NET does).

Mixing dot notation and query syntax in a single query can look messy, as shown here:

So, I prefer to settle on just one style of LINQ declaration for any particular query, or to use intermediate variables and separate the query into parts (this is especially useful on complex queries as it also provides clarity; being terse is cool, but it is unnecessary, and a great way to get people to hate you and your code).

The Distinct() method is not the only LINQ method that has no query syntax alternative, there are plenty of others like Aggregate(), Except(), or Range(). This often means dot notation wins out or is at least part of a query written in query syntax. So, thus far, dot notation seems to have the advantage in the battle against query syntax. It is starting to look like some of my colleagues are right, query syntax sucks. Even if we use ordering or grouping, dot notation seems to be our friend or at least is no more painful than query syntax:

However, it is not always so easy. What if we want to introduce variables, group something other than the original object, or use more than one source collection? It is in these scenarios where query syntax irons a lot more of the complexity. Let's assume we have another collection containing newsletters that we need to send out to all our people. To generate the individual mailings, we would need to combine these two collections6.

I know which one is clearer to read and easier to remember when I need to write a similar query. The dot notation example makes me think for a minute what it is doing; projecting each person to the newsletters collection and, using SelectMany(), flattening the list then selecting one result per person/newsletter combination. Our query syntax example is doing the same thing, but I don't need to think too hard to see that. Query syntax is starting to look useful.

If we were to throw in some mid-query variables (useful to avoid calculating something multiple times or to improve clarity), or join collections, query syntax becomes really useful. What if each newsletter is on a different topic and we only want to send newsletters to people who are interested in that topic?

I know for sure I would need to go look up how to do that in dot notation7. Query syntax is an easier way to write more complex queries like this and provided that you understand your query chain, you can declare clear, performant queries.

 

In conclusion…

In this post I have attempted to show how both dot notation and query syntax (aka fluent notation) have their vices and their virtues, and in turn, armed you with the knowledge to choose wisely.

So, think about whether someone can read and maintain what you have written. Break down complex queries into parts. Consider moving some things to lazily evaluated methods. Understand what you are writing; if you look at it and have to think about why it works, it probably needs reworking. Always favour clarity and simplicity over dogma and cleverness; to draw inspiration from Jurassic Park, even though you could, stop to think whether you should.

LINQ is a complex feature of C# and .NET (and all the other .NET languages) and there are many things I have not covered. So, if you have any questions, please leave a comment. If I can't answer it, I will hopefully be able to direct you to someone who can. Alternatively, check out Edulinq by the inimitable Jon Skeet, head over to StackOverflow where there is an Internet of people waiting to help (including Jon Skeet), or get binging (googling, yahooing, altavistaring, whatever…)8.

And that brings us to the end of this series on LINQ. From deferred execution and the query chain to dot notation versus query syntax, I hope that I have managed to paint a favourable picture of LINQ, and helped to clear up some of the prejudices and confusions that surround it. LINQ is a powerful weapon in the arsenal of a .NET programmer; to not use it, would be a waste.


  1. Just the thought of the nested method calls or high number of placeholder variables makes me shudder 

  2. I guess LIQ was too suggestive for Microsoft 

  3. That statement is an absolute, Obi Sith Kenobi 

  4. I am definitely leaving the nested static methods approach to you as an exercise (in futility 

  5. Though if you changed the person variable to p, there is less typing in the query syntax , if that is a metric you are concerned with 

  6. Yes, a nested foreach can achieve this simple example, but this is just illustrative, and I'd argue cleaner than a foreach approach 

  7. That's why I cheated and wrote it in query syntax, then used Resharper to change it to dot notation for me 

  8. Back in my day, it was called searching…grumble grumble 

Debugging in LINQPad

If you have been reading my blog over the last few months you will no doubt be aware that I am a regular user of LINQPad. I do not have any commercial involvement with LINQPad nor its creators, I just really like it. Recently, I decided to try out the latest release, which adds integrated debugging to the already feature rich tool. This amazingly powerful new feature adds yet another reason why this application should be in every developer's arsenal, regardless of experience and ability (it is a great learning tool for students). Here is a brief overview of this new feature, which is available with the premium license (currently on sale for $85 at time of writing; it may not be the case as you are reading this).

When running the latest LINQPad, the debugging feature adds some new buttons to the familiar toolbar. All the debugging features are available for both statement and program-based queries in C#, VB, and F# (not expressions or SQL languages). The first new button is the Pause button, also known as Break. This works as you might expect, pausing the current code execution. The other two are to specify how exceptions should be handled, informing  the debugger to break on unhandled exceptions and when exceptions are thrown. Breakpoints can be added by clicking in the margin to the left of the code or pressing F9 when the caret is on the desired line.  When a breakpoint is active on a line, it is indicated as a large red circle. For those who regularly use Visual Studio, the breakpoint and general debugger experience will be familiar.

Pressing F5 will run the query (or selected lines) as usual, but now, any breakpoints set on executing lines will cause the code to break. At this point, LINQPad will reveal some familar and not-so-familiar tools for debugging the code. General status information is displayed at the bottom of the LINQPad window, showing things like whether the code is executing or paused, whether the debugger is attached or not, and the process ID.

The next code statement to execute is highlighted in the code with a yellow arrow in the margin (in this case, overlaid on the breakpoint circle), and the code highlighted in yellow. In the lower left portion of the screen, we can see local variables and executing threads. We can also set up our own watches as necessary. Any objects in the Locals and Watch tabs can be expanded using the + glyph to reveal their constituent values. As in Visual Studio, these tabs allow the expansion of just-in-time LINQ queries so you can delve into the deep dark secrets of your code. However, you can also take advantage of LINQPad's fantastic dump feature and dump any value out to the Results tab on the right. If you want to control how far down the object graph a dump will go, you can modify the Dump Depth using the + and - controls in the column header.

The `Dump` output for the `range` variable
The Dump output for the range variable
Specifying the depth of the dump
Specifying the depth of the dump

For more information on LINQPad and its many features, check out the LINQPad website (http://linqpad.net). In my opinion, whether you use the free version or one of the paid upgrades, you will have one of the best coding utilities available for .NET.