Posted in:

I had the privilege of speaking at a brand new Pluralsight Meet the Authors event in London last week, organized by Elton Stoneman. I decided to do a LINQ tips and tricks session based on my new Pluralsight course “More Effective LINQ”.

I had just enough time for seven tips, so here they are…

1. Use LINQPad

I’m a huge fan of LINQPad, and I’ve banged on about it enough on this blog before so I won’t say much more than buy yourself a license! You won’t regret it. A fantastic light-weight mini-IDE for all your code experiments.

2. Think in Patterns

Pretty much anywhere you are using a foreach loop is an opportunity to use LINQ. You just need to spot the patterns. For example this code snippet below:

string beginsWithJ = null;
foreach (var person in people)
{
    if (person.StartsWith("J"))
    {
        beginsWithJ = person;
        break;
    }
}

… can be replaced with a single line of LINQ …

string beginsWithJ = people.FirstOrDefault(p => p.StartsWith("J"));

In fact, if you’ve been using LINQ for a while, seeing these kind of transformations will start to come naturally to you. ReSharper is also very good at pointing out where you can refactor a foreach loop into a single LINQ statement, making your code much more succinct and expressing your intent more clearly.

3. Use Pipelines

Where LINQ really starts to shine is when you start to combine and “chain” together these LINQ extension methods to form what are sometimes called “pipelines”. To show off how powerful they can be, I solved one of my LINQ challenge problems. Why not try one of these yourself and compare your answers with mine?

4. Be Lazy

Take a look at this method, which does some processing on a bunch of filenames. Can you see a problem?

void SomeFunction(IEnumerable<string> fileNames)
{
    fileNames.ToArray()
    foreach (var s1 in fileNames)
    {        
        Console.WriteLine(File.ReadAllText(s1));
    }

    foreach (var s2 in fileNames)
    {
        Console.WriteLine($"{s2} is {new FileInfo(s2).Length} bytes long");
    }
    
    var longestFileName = fileNames.Max(s => s.Length);
    var shortestFileName = fileNames.Min(s => s.Length);
}

If you said “multiple enumeration”, you’re right! This method enumerates through the fileNames sequence four times. That might not be an issue, if filenames is for example a List<string>. But because of “deferred execution” we don’t know whether each time through we’ll get the same results. We also can’t be sure that it will enumerate quickly, because there could be disk IO going on when we enumerate. In this example, a quick fix would be to call ToList on fileNames to get it in memory, allowing us to safely and quickly enumerate through several times.

5. Extend LINQ

LINQ comes with a fantastic built in library of extension methods, but there are times when you need something that is missing. For example LINQ doesn’t have a MaxBy method, which you need for doing things like find the book with the most pages. Now it’s true that you can often find clever ways of combining existing LINQ operators, or use Aggregate, the Swiss Army knife of LINQ methods, but you can end up with poorly performing or hard to read code if you do that.

So it’s a great idea to learn how to create extension methods, as they can greatly benefit the readability of your code. The good news is that for LINQ, most of the extension methods you could ever want have already been created for you in the fantastic MoreLINQ library. If you haven’t tried it yet, I can highly recommend it. It turned out to be super helpful when I solved the Advent of Code challenges in December.

6. Optimize Performance

How fast is LINQ? If you need the fastest performance, is it better to abandon LINQ and just use for loops for the ultimate speed? In this demo I showed how there is a small performance penalty for using LINQ but it can be overcome by using libraries like LinqOptimizer or using Parallel LINQ (PLINQ). Check out this very simplistic performance test to get a feel for what kind of performance gains are possible.

7. Understand the Underlying SQL

In this tip I showed how although LINQ to Entities usually creates excellent SQL queries on your behalf, it is in fact quite easy to accidentally do things that result in dreadful performance. A classic blunder is to insert a ToList into the middle of a query because you did something that Entity Framework couldn’t translate to SQL. So in this dreadful example we end up retrieving the entire contents of the Albums table into memory, and then go on to perform numerous additional SQL queries in order to request the artist name for each album.

Albums
   .ToList()
   .Where(a => a.Artist.Name.Count(c => c == ' ') > 2)
   .OrderBy(a => a.Artist.Name)
   .Select(a => new { Artist = a.Artist.Name, a.Title })
   .Take(5)

The moral of the story is to understand what a Select N+1 problem is, and what can cause it, and to keep an eye on what SQL is actually being generated under the hood by Entity Framework (or LINQ to SQL).

Want More?

Obviously 40 minutes was nowhere near enough time to say all that needed to be said on these topics, so do check out my More Effective LINQ Pluralsight course if you want to go deeper.