5 ms·
I am a big fan of LINQ but it gives and takes away complexity at the same time. It makes a 'whole class of things that you would have to do with loops' go away
by joshuaellinger 13y ago
I am a big fan of LINQ but it gives and takes away complexity at the same time.
It makes a 'whole class of things that you would have to do with loops' go away.
Once you are comfortable with the syntax, it makes code a lot more readable.
But you lose track of when and where things are getting executed.
For example, it is easy to make something that you intend to execute inside of
SQL server run inside of C# code. And then, all of a sudden, string comparison
is case-sensitive.
You wind up having to context switch between procedural and set mentality without
the same kind of visual cues you used to get.
- jmcqk6 13y agoI've ran into this a little bit as well, but I feel the benefits far outweigh the disadvantages. Once you understand the basics, your code becomes so much cleaner. You do have to be careful for hidden pitfalls, like an accidental N+1 select against the database, but even that is usually fixed pretty easily if you simple call .Include() in the original expression. From the article, I really don't understand the concept of not updating your knowledge of Linq. It's been out for years now. And then the first concern is with performance? That sounds very problematic to me. There are a lot of .NET developers out there that are still stuck in the previous decade. There are a lot of shops that are still on 2.5, which is too bad, because that was when the framework and C# really started to take off.
- ryanmolden 13y agoThere is also some trickiness around when things get executed. If you return an IEnumerable<T> that is really a LINQ query, and the caller doesn't immediately enumerate it, they run the risk of getting exceptions when they do enumerate if you mutate the collection the query runs over. You can of course force its evaluation, but that also loses some of the benefits of the laziness. Bring in multi-threading and even if you properly synchronize collection access (and that is hard to do without custom collection types, since LINQ is like the honey badger w.r.t. synchronized access) you can still see the collection modified exceptions talked about above.
- kryten 13y agoIt's also really easy to shoot yourself with unless you understand everything and think each problem through. Four killer issues I've seen so far: We had a major production performance issue which turned out to be a stray ToList which was causing a massive memory ballooning. Didn't get noticed in test as the test cases passed but it hit prod and 2000 users bashed it and tried to allocate 20Mb each causing our cluster to shit a brick. Null reference exceptions! There are so many dereferencing operations in an average LINQ expression that you really have no idea which one is blowing if it goes pop in production in release config. People using .Single(...) and getting more or less than one result back. So frustrating. If you push an IEnumerable<T> over an interface boundary the performance and memory semantics are not preserved and you end up with a leaky abstraction. These are shits to resolve. Example: queries executing inside the view which is outside the transaction scope. We've had to ban it in some circumstances.
- usea 13y agoI agree with your pain points. We try to use ICollection in our APIs instead of IEnumerable, since the latter can have surprising semantics like being a wrapper for some operation which may not be valid anymore, or might be slower than you expect to do things like .Count(). IMO it's really not best for transporting across interface boundries in the most common case; only when you're specifically trying to avoid having the whole collection in memory or something like that. Another thing that can help is this wonderful May<T> library[1]. It a great option type[2] for .NET. It helps make operations more composable. [1] https://github.com/Strilanc/May https://github.com/Strilanc/May [2] http://en.wikipedia.org/wiki/Option_type http://en.wikipedia.org/wiki/Option_type
- venomsnake 13y agoBut you lose track of when and where things are getting executed. That is the curse of modern software development. But LINQ is minor offender here compared to some kinds of remoting, code reflects from endless xml configs wrapped in gazillion interfaces and some inventive orm-s. Sadly our tools have not kept up with the complexity that is external to the executing code. The debugger in the pre- inversion of control/bean injection days was almighty because you had all of the program state in front of you. A little known fact is that if you wrap Java/C# code in enough unit tests you get Haskell.
- joshuaellinger 13y ago> A little known fact is that if you wrap Java/C# code in enough unit tests you get Haskell. It is enough to make you want to switch to a pure message passing system, isn't it?
- MichaelGG 13y agoThe "execute inside SQL but ends up in C#" is because of the terrible decision of the C# language designers to make reified code (expression trees) have the exact same syntax as lambda functions that are normal code. If you had to tell the compile if you wanted code or a tree, that problem would be solved. It'd also be one step closer to allowing type inference for lambdas assigned to locals.