Ten shocking reasons to stop using LINQ. Some may surprise you.

Okay, the title is a total troll, I just thought it would be funny to create it as link-bait in light of a recent Scott Hanselman article about how these things are the death-throes of the internet. Sorry Scott!

When Microsoft first introduced these Haskell-inspired query operators in the 3.5 release of the .net framework, like so many others, I was ecstatic. I watched every video Erik Meijer put up on channel9, started reading about the functional concepts that underpinned the new features, learned about the compiler-generated state machines and deferred execution, but most prominently I used LINQ everywhere.

I call it the law of the instrument, and it may be formulated as follows:
Give a small boy a hammer, and he will find that everything he encounters needs
pounding. — Abraham Kaplan

This is commonly referred to as the Law of the instrument. It’s a bit cliché, but in this instance it was true, I had one hell of a hammer, and before me all I could see was a vast IEnumerable<Nail> begging to be sorted, and reversed, and selected. A few years and many a mangled and bent nail later I have come to regard Language Integrated Query, much like its functional forebears, as perhaps too powerful for the average programmer. The allure of function composition, the terse yet readable query operators, the syntactic sugar of the more SQL-like declarative syntax are all amazing advances to the language, and the minds behind it are truly remarkable. Sadly, not all advancement is to the betterment of all, and I’m of the opinion that LINQ is one such tool which has been abused for ill.

To be fair, many of these justifications stem from the fact that most .net developers did not do their homework when 3.5 first came out, either out of laziness or because they were trapped in an environment with antiquated tooling, and now the educational content is simply harder to find. It’s not new and exciting anymore, so nobody writes about it.

  1. Seductively easy to not filter and sort at the database level.

This is a huge pet peeve of mine which is why it is at the top of my list. I cannot fathom the laziness that drives a person to write code like this:

    var products = FetchAllTheThings()
    	.Where(item => item.Price > 500.00m)
    	.OrderBy(item => item.Price)
    	.Take(10);
    

Seems harmless enough, but let’s break this down. Firstly, I’m taking it as assumed that FetchAllTheThings() is responsible for reading from the database (possibly over the network) but is not backed by Linq-to-sql or Entity Framework, because that would enable this query to actually be rendered to the database. In this case I’m talking specifically about technologies which are not linq-friendly like direct reads from an ADO.net SqlReader or some homegrown micro-ORM that your best and brightest devs put together 10 years ago to protect the less savvy from harming themselves or others.

So with those basic constraints in mind, let’s take a look at the above query in more detail. The result set is potentially huge, could continue to grow over time, and we have no control over it. We throw away most of the results between the where and take, so all that I/O and ORM work was essentially a complete waste. Worst of all, none of this needs to be expressed as managed code; these are all directly mapped functions of SQL server. I will be the first to admit that t-sql is far from an elegant language, but in the domain of sorting, filtering, projecting, aggregating, and indexing it remains the most efficient way to leverage the power of your relational database. Linq-to-objects abstracts the details away, but this code is non-trivial and took only seconds to write. The performance implications are huge, and the amount of thought that needed to go into writing it was dangerously low.

This pattern of unapologetic disregard for coding decency reminds me a lot of Hedonism Bot from Futurama. In my head, this is how I imagine them.

Yes, bring me all the data, I shall pick what I like and discard the rest.

I trust the memory-orgy heap has been garbage collected and made ready for my next indulgence.

From one million records I need only one. How decadent!

  1. Complex expressions quickly become unreadable.

Repeat after me: “instructions are for processors, code is for humans”. If the next person who comes along after you cannot quickly comprehend what your code is doing then you have done him or her a disservice, and should feel bad about that, unless you are a sociopath, in which case, as always, you feel nothing.

There is something magical about LINQ that takes all rules about formatting and throws them out the window. The generally accepted pattern is to put each query operator on a separate line. This is seen as preferential to the epic one-liners whic happen just as often, but I would argue that both are code smells.

        var seq = setOfThings
                .Where(w => w.IsSpecial || w.HasValue || w.HighPriority)
                .OrderBy(x => x.Price)
                .ThenBy(t => t.Discount)
                .Skip(10)
                .Reverse()
                .Take(50)
                .Select(s =>
                    new
                    {
                        Name = s.Name,
                        Price = s.Price.ToString("D")
                    }).ToList();
    

It’s actually quite hard to produce knowingly ugly example code that gets this message across. It’s like trying to will yourself to spell something incorrectly…

At any rate, as readable and concise as the query operators are by themselves, the composition is something that has quickly spiraled out of control. Imagine how this would be to debug (which I’ll touch on later).

I think a more hideous example is the mixing of the declarative “query syntax” and the extension methods where there is no support for things. Like FirstOrDefault() and ToList(); You get these extra parenthesis that just wrap around your weirdly out of place code haiku as below:


   var seq = (from thing in setOfThings
              where w.IsSpecial || w.HasValue || w.HighPriority
              orderby thing.Price, thing.Discount)
              .Skip(10) // oops, we've run out of query support.
              .Reverse()
              .Take(50)
              .Select(s =>
                    new
                    {
                        Name = s.Name,
                        Price = s.Price.ToString("D")
                    }).ToList();

    

Okay, this post has gotten a bit wordy. I had no idea I was bottling up all this nerd-rage about LINQ. I’m definitely not going to get to all 10 without people losing interest, so I’ll break it up into multiple posts.

Tune in next week for the next exciting installment: “More ways LINQ causes code-tumors.”

3 thoughts on “Ten shocking reasons to stop using LINQ. Some may surprise you.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s