DEV Community

Cover image for C# and .NET Core Appreciation Post. The most beautiful piece of code I have ever seen... this month!

C# and .NET Core Appreciation Post. The most beautiful piece of code I have ever seen... this month!

Sdu on December 09, 2019

Disclaimer! This post is a little bit ridiculous and over the top but I'd be doing injustice if I do not share my thoughts on how the jo...
Collapse
 
maartyl profile image
maartyl • Edited

I hate C# LINQ query syntax. It is completely pointless. You can always use LINQ methods directly.

This is a warning to anyone who thinks it's cool.

  • It gives you nothing. It's not shorter, it's not more readable, it's not faster... It's actually harder to refactor and maintain.

  • I used to love them at first: they look so cool, right? Well, I've been using C# for many, many years, and the experience has always converged to: avoid them, you will regret the temptation. - Use lambdas directly instead: Lambdas are great.

  • Nice simple example: Often, you need to split the query into 2 places: super simple with normal syntax: you just copy part of it elsewhere, keeping it as IEnumerable/IQueryable. - With LINQ syntax, you now have to figure out how to split the nonlinear query, the beginning and end is always repeated, you have to type them over..... Zero added value, only problems. After all, its design was inspired by the ergonomically ~worst language: SQL. (I admit, this is opinionated; what I say about LINQ syntax: not so much)

  • Most importantly: Not all LINQ/extension methods have LINQ syntax, so now you have to mix it, and it's completely horrible.

  • Also, it has nothing to do with .NET Core: It's been in C# since before 2008.

btw. Don't get me wrong: LINQ extension methods are great... except, for no good reason, C# decided to call all the methods differently than every language ever - but the LINQ syntax was 100% a mistake.

Collapse
 
sduduzog profile image
Sdu

I love how this came after I had seen this comment firsthttps://dev.to/fernandomondo/comment/ilae or else I would have been devastated by now. I have rewriten that piece of code in my project using lambda, that whole function is made up of just two lines now. In fact, this is a great neede eye-opener.

Also, it has nothing to do with .NET Core: It's been in C# since before 2008
These guys were in the future already! That's amazing!

Collapse
 
brunnerh profile image
brunnerh

The LINQ syntax has some advantages in certain cases. It can be both more readable and shorter if your query is complex (sub-queries/grouping/cross products) or requires temporary state (those tend to not be translatable to SQL and are usually done in memory).

Lambdas add a ton of noise in general because of punctuation (parentheses, braces, arrows) and you have to re-declare the item variable in every chained call.

For a simple query like this using a navigation property and a lambda a simpler, as shown.

It is about knowing when and how to use a tool; most tools are not categorically "useless".

Thread Thread
 
sduduzog profile image
Sdu

@maartyl here goes me proving you wrong! 😅

But with this argument, we all come out as winners. The abundance of tools means there's more than one way to do the job.

Thread Thread
 
maartyl profile image
maartyl

@brunnerh Thank you for a counterexample. I agree that with complex queries they probably become more worthwhile, and at some point almost certainly are nicer than using lambdas directly, however, I would argue a complex query should not even be kept in the code, usually.

(I should note, I'm not too familiar with LINQ to SQL)

  • It can probably be refactored into multiple reusable functions. (and the moment you do this, the LINQ syntax will be worse again) - It probably should be, and using LINQ syntax makes it harder to do so. - At least to me, large queries seem quite hard to read and reason about, whereas 'composing function{s, calls}' is much nicer (I admit, this is probably a matter of opinion, but I often reuse 'subqueries' and that is generally better).

  • (if it is LINQ to objects) I've never seen that complex queries actually needed - Usually, there is no problem using 'lambda' syntax. (although, to be fair: I haven't always tried, but the trying would probably cost more time overall, than just always using one pattern for everything - especially one that INTERRACTS well with everything else in the language)

  • (if it is LINQ to SQL) If it is complex enough: it should probably not be written in C# at all, as LINQ syntax is very limiting, and I've repeatedly found it does not support things I needed, and had to rewrite it in raw SQL anyway.

  • Writing a query that mixes SQL and memory (does not translate to SQL) feels like a code smell to me. I understand there is some benefit to having a purely SQL query, which you then change to actually run in memory, without rewriting it, but it feels more like it would happen by accident, and not behaving as you expect it to. - I like to have a clear separation of what runs where, and generally clear separation between all things that need their own 'governing'.

  • Lambda noise was never much of an issue for me, but I admit this is a good point. I use lambdas everywhere for everything, but someone not as used to them might find it annoying.

Overall, there probably is some use for them (for actual, complex queries) but in my experience, I don't think I've ever encountered it. I guess they may be great for someone else, I just cannot imagine it...

PS: No idea why dev.to notified me the first time, but not again. I've only found this because I came back to report on my own findings.

Collapse
 
maartyl profile image
maartyl • Edited

I'm glad you saw the other comment first. ^^ I would have hated to make you feel devastated. I just wanted to deter others from repeating my mistakes. ^^ (and clearly, I'm not the only one :D) - I was probably just a bit harsher, because that temptress burnt me many times, before I finally learned. XD

Collapse
 
maartyl profile image
maartyl • Edited

PS: I would love it, if you could prove me wrong. ^^
- I would love to use them... it's just never been worth it.

Collapse
 
sduduzog profile image
Sdu

This is going to be hard 😅 You have years of experience, I have a few weeks. Unless they introduce a breaking change that we'll most likely learn at the same pace, there's no way I can defend LINQ as of yet

Thread Thread
 
maartyl profile image
maartyl

I want to wholeheartedly thank you for this article. I considered LINQ syntax so useless, I forgot about it when trying to figure out how to bend C# to my needs. Thanks to you, I started to wonder what LINQ actually could be useful for, and it has a use!

It can be used to add monadic 'do' syntax sugar. I missed this for a long time, creating all sorts of workarounds. Inventing ways to use the language in manners the language was never meant to be used. (Not stopping now, lol)

Now, I finally found a use for LINQ syntax. I didn't implement it yet, but I am nearly certain it's possible. Implementing the specific SelectMany on my custom type, I should be able to write 'nested' bind calls (thus able to reference previous variables) as a flat sequence of operations.
(I just hope there is not some horrible trap, like there is in mutable async blocks (which makes them useless as generic monad syntax))

var r = from a in m()
        from b in m()
        let aa = a + b
        from c in m()
        select new { aa, b, c };

Imagine m() to be some monadic expression.

If this works, my code will be so much more readable and maintainable thanks to this. ^^ - And if it wasn't for you, I would have never thought about this. So, thank you. ^^

Collapse
 
bdwakefield profile image
Benjamin D Wakefield

I am a fan of the fluent API. I never much liked the 'SQL-like' variants. Joins especially were ugly. Getting back an IQueryable and working with that till you need the data is much preferred, IMO.

Collapse
 
xboxplayer13 profile image
Cold Turkey • Edited

Here is your example written using an async method and lambda expression syntax:

private async Task<User> GetUserByTokenAsync(string token) => await _context.Users
.Join(_context.Tokens.Where(t => t.Body == token), u => u.Id, t => t.UserId, (u, t) => u)
.SingleOrDefaultAsync();

Note: you will need to include additional namespaces for the async extension methods on IQueryable<> and for Task<>

Collapse
 
fcastells profile image
Francesc Castells

Drop the async and await keywords for better performance.

Collapse
 
sduduzog profile image
Sdu

At some point, I'm looking forward to benchmarking the alternatives i.e. Fluent vs Lambda, Async vs Sync. I'm certain there's no one answer for every use case

Thread Thread
 
vegardst1 profile image
zend

Fluent translates to the same underlying Linq statements and concequently the same generated SQL. Note: The await keyword can be used in front of the parentheses.

Async code frees up the thread to do other things while it's waiting for the slower database network roundtrip. You are trading a small setup cost for starting the state machine handling the async processing, but that's it.

Thread Thread
 
sduduzog profile image
Sdu

So basically async statements are useful for performance in situations where the data being processed is rather large

Thread Thread
 
fcastells profile image
Francesc Castells

I agree that using the Async flavor of the SingleOrDefault is generally the preferred way as it improves massively the amount of load a single server can absorb. My comment was simply that the proposed method does not need to be async (contain the keyword async in the signature) as there is no need to await tasks and execute continuations within the function. So, basically, you can directly return the Task returned by SingleOrDefaultAsync without awaiting it as the caller will await it.

Thread Thread
 
illuminatecode profile image
illumiNateCode

@Beautus S Gumede on "So basically async statements are useful for performance in situations where the data being processed is rather large"

(Not seeing proper "Reply" function on mobile)

That's one example where async is advantageous, and there are many, and it gets complicated pretty quick. I would recomend doing a deep dive on Asynchronous and Threading, but know that they are two very different subjects (although their impacts on your code can appear similar).

There's a pretty common analogy used to explain threading involving a chef/cook that I find to be a good foundation for internalizing the concept. Examples are all over Google for that. This article gives you a reasonable overview with some good detail, IMO: medium.com/swift-india/concurrency...

Happy coding!

Thread Thread
 
cmoya212b profile image
cmoya212b

@Beautus S Gumede In a highly performant scalable web app, async/await allows .NET to momentarily give the underlying thread to a different incoming request while waiting on IO (such as a database call). It makes .NET prolly the most scalable stack out there when done right. NodeJS does similar things with its singlethreaded event loop.

Collapse
 
jandonnermayer profile image
JanDonnermayer

This is much better understandable I think

Collapse
 
alienroid profile image
Alienroid • Edited

Great! Now try lambda expression, you will be happier:)

Collapse
 
sduduzog profile image
Sdu

I did 😅 it looks way better if you're working on a single table

Collapse
 
fernandomondo profile image
Fernando Mondo

You can use navigation properties and lambda to improve your code.

Something like:

public class User
{
public virtual Token Token { get; set; }
}

And then:

_context.Users.SingleOfDefault(u => u.Token.Body == Token);

Thread Thread
 
sduduzog profile image
Sdu

Ok, this actually looks cleaner and more concise. I'm assuming from this that the virtual property relation to users is setup automatically because both entities exist in one context. I'm puzzled by how this really works

Thread Thread
 
sduduzog profile image
Sdu

Here's another strange thing I'm picking up from this snippet, the fact that the property name and its type can just be exactly the same without C# complaining. I've been experimenting areas where such is possible. public virtual Token Token;

Collapse
 
kbiel profile image
Ken in NH

I will add my voice to the others here who recommend avoiding LINQ syntax. Most MS development shops have gotten away from LINQ syntax and your query shows one good reason:

It might be more efficient for the where to execute before the join. With the LINQ syntax you can do it but it looks odd.

var user = (from t in _context.Tokens
    where t.Body == token
    join u in _context.Users
        on t.UserId equals u.Id
    select u).SingleOrDefault();

Now compare:

var user = _context
    .Tokens
    .Where(t => t.Body == token)
    .Join(
        _context.Users,
        t => t.UserId,
        u => u.Id,
        (t, u) => u)
    .SingleOrDefault();

For your specific case, I suggest you turn _context.Tokens into an IDictionary<string, Token> or even IDictionary<string, User> where the token body is the key. Then your method looks like this:

#nullable disable
private User GetUserByToken(string token)
    => _context.UsersByToken.TryGetValue(token, out var user)
    ? user
    : default;
#nullable restore
Collapse
 
sduduzog profile image
Sdu

I have a question with this one. How would my DBContext class look like, will this UsersByToken field have to be mapped to a table in the bd too?

One thing that has been mentioned a couple of times is that I should consider using IDictionary, I'm looking forward to it

Collapse
 
kbiel profile image
Ken in NH

Ah. I did not know what _context was and I was not aware you were using EF. I assumed _context was some kind of property bag or class you controlled. So, the answer is no you would not have a UsersByToken property on your DbContext and it would not map to the database. Instead it should be a local field/property in your login class. Now that I understand you are making round trips to the database, you will find IDictionary<> to be even more efficient than your current code, but you will need a fall back to retrieving the user from the database if it is not found associated the provided token in your dictionary.

Thread Thread
 
sduduzog profile image
Sdu

Thank you for this. Seems like IDictionary and IQueryable are my best bet for getting the most out of EF

Collapse
 
alivaterocket profile image
Alivate • Edited

You shouldn't use the join keyword, a second "from" is best:

i) you get to use the equality operator;
ii) you can arrange the operands any way you like;
iii) you can add on the DefaultIfEmpty() function on the end to change to a left join and vice versa.

private User GetUserByToken(string token)
{
    var user = (from u in _context.Users
        from t in _context.Tokens.Where(T => t.UserId == u.Id)
        where t.Body == token
        select u).SingleOrDefault();

    return user;
}

Also, I always use the LINQ format instead of chained functions. Simply because it's more readable.

Collapse
 
sduduzog profile image
Sdu

This is so true. The join was unnecessary 🤔 I was kind of suprised to why the Lambda version I switched to seems shorter, it was because it didn't use a join 😬

Collapse
 
troywitthoeft profile image
Troy Witthoeft

Yeah... LINQ is solid stuff. Had a dev on my team give the same reaction a few weeks back.

Collapse
 
neilpearson73 profile image
NeilPearson73

Now if you want it to run 10 times faster, drop entity framework and use dapper

Collapse
 
sduduzog profile image
Sdu

Challenge accepted meme

Collapse
 
terricide profile image
Terricide

If you want better performance but still strongly typed you could look at linq2db

Collapse
 
tehwardy profile image
Paul Ward

The next step up from this is to combine this with webapi odata, that way the framework generated this sort of linq expression for you from a URL.

The irony here is the discovery .net core 3 and the EF core libs that make this possible are right now in a worse state than the ef6 and .net 4.x stuff regular .net people just migrated from.

Give it a couple years though and this will be special under .net 5 when you start notice it's literally write once, run anywhere.

Collapse
 
tomholden1 profile image
Tom Holden

LINQ, like SQL, is declarative.

Collapse
 
sduduzog profile image
Sdu

It's interesting that I didn't' use the term 'declarative' but now that you've mentioned it, everything makes sense. I like declarative code :D

Collapse
 
tomholden1 profile image
Tom Holden

Roll with it. Read 'T-SQL Fundamentals, Third Edition' by Itzik Ben-Gan. Work through all the examples. You'll see why "SELECT" really belongs after "FROM" and "WHERE" (LINQ knows!). Find a good LINQ resource and work through more examples (LINQ to this, LINQ to that...). Don't worry about the lambda expressions underpinning LINQ syntactic sugar at this time. Microsoft has a fine offering. Visual Studio is the best IDE on the market. DOT NET "Core" is platform independent. For Web, perhaps check out angular.io (a Google framework) underpinned by TypeScript language (the "C# guy" invented TypeScript). Angular is organized, TypeScript is awesome, and it all downcompiles into the correct JavaScript for the device requesting your "stuff." There are dollar bills for your bank account on the Microsoft train. A lot of the other stuff is budget busting, shifting sand. In my experience.

Thread Thread
 
maartyl profile image
maartyl

I 100% agree that SQL got select wrong, and it should be at the end!

As for VS being the best IDE, I honestly find using Kotlin and IntelliJ Idea more pleasant.

Collapse
 
eliasbobadillav profile image
elias

I felt it bro, C# is easy and beautiful

Collapse
 
wozsoftware profile image
Warren

Linq is epic and versatile. Here is a sample of me combing two Lazy objects building a new lazy result without executing them via using .Value, via linq monad extensions i created

Var lazyresult =
from a in lazyThing1
from b in lazyThing2
select a + b;

Collapse
 
vekzdran profile image
Vedran Mandić

Lovely article Beautus! As other commenters mention I also remember this thrill and power when witnessed at first. It is truly an amazing feeling and I share your excitement with JSX. It is great to hear you have bound with feature of a language such as LINQ as that will guide you to discover other amazing features like maybe the newest pattern matching in switch clauses? Which again might lead you to a totally different but .NET language as F#.

Keep on mind, LINQ syntax gives you range variables which you do not have in the lambda function syntax. The LINQ syntax eventually ends up “compiled” into lambda syntax, i.e. just a bunch of methods, read a ref explanation here: stackoverflow.com/a/20573973

If you are interested to understand the fantastic IEnumerable “driver” of the LINQ and how to literally reimplement then google for EduLINQ by Jon Skeet. Harnessing the yield keyword with iterators and generators is what made love C# even more. Afterwords you can google how LINQtoEntities works (thats the one you use with _context), hint/spoiler its an amazing compiler that helps it with “translation” into SQL with a ton of expression visitor pattern implementations composed to build a compiler pipeline... uh :-)

Also a side note on async as I am very allergic to that topic :-), it will make your code slower but it will take care of memory better in longer (scale up scenario) shots. It is not a silver bullet and it has nothing to do with increasing performance at all (only maybe decreasing it). Google Stephen Cleary and his blog for more info.

Cheers,
V.

Collapse
 
sduduzog profile image
Sdu

I just read through the stackoverflow link and I'm stunned. But through that revelation, I had to wonder how performant a Lambda api would fare up against 100 million rows of data as opposed to using a raw query

Collapse
 
vekzdran profile image
Vedran Mandić • Edited

Hey. Your thoughts stream to right questions! Actually EF is a lot slower than raw cons (or Dapper queries), for a test in the following link 10x to 15x times, but the test is not a real world scenario, i.e. do not infere that Dapper or ADO is better, as speed is just one treat. Read more about the test: exceptionnotfound.net/dapper-vs-en...

Also EF has to cache your lambdas after translating them for the first time into SQL strings. Also note some queries can not be cached due to their dynamic construction (you can build up a query with multiple C# statements) such as any query using a .Where(x => someNumbers.Contains(x.Id)), as the SQL “where in” (it is actually a ton of OR statements if you look into SQL) part is actually calculated each time.

Using EF is certainly not a question of speed.

Collapse
 
apung profile image
Abdul Gaffur A Dama

Use lazy loading proxy from Microsoft.EntityFrameworkCore.Proxies package, so you can get better sleep

Collapse
 
sduduzog profile image
Sdu

I'll look this up! Thanks

Collapse
 
balassamarton profile image
Márton Balassa

Lazy loading navigations can easily lead to accidental N+1 queries. You can explicitly include navigations with DbSet<>.Include.

Collapse
 
juancri profile image
JC Olivares • Edited

I like LINQ syntax, but I think it's not really necessary here. I'd write it as:

private User GetUserByToken(string tokenBody)
{
    var token = _context.Tokens.SingleOrDefault(t => t.Body == tokenBody);
    if (token == null)
        return null;

    var user = _context.Users.SingleOrDefault(u => u.Id == token.UserId);
    return user;
}

Of course, this is a subjective matter, so any approach is OK if the team is fine with it.

Collapse
 
frankszendzielarz profile image
Frank Szendzielarz

If you like that, check out rx.net reactive extensions for linq

Collapse
 
shalokshalom profile image
ShalokShalom • Edited

You have never written in a functional language, true that?

FSharp is far superiour and tons more declarative as CSharp ever can be.

It is a superset, csharp has nothing that fsharp has not and the other way around has fsharp everything that csharp has and uses barely anything of it, since every fsharp dev knows, it is the by far weakest part of the language.

Sadly, scientific evidence counts less in a world full of self declared "computer scientists"

fsharp.org/testimonials/

Collapse
 
rezanid profile image
Reza • Edited

Welcome to C# I remember this feeling. In fact, it has happened to me a few times in my career. Always be aware of the industry, but don’t take it too seriously. It’s people like you and I that are shaping the industry. Don’t be scared to make mistakes, but try always to make software that you can be proud of and share what you learn with others.
Side note: Java is also a wonderful high level language with tons of resources around it now, but one of the forces that made it popular was the amount of money that was invested on advertising it. I suggest you check this video on YouTube, who knows you might fall in love with F# one day ;)
youtu.be/QyJZzq0v7Z4

Collapse
 
sduduzog profile image
Sdu

This gives me an idea. After I'm done with the project I've started with. I want to create a clone or similar with F#. With .NET Core also, I think that's going to be very interesting

Collapse
 
ygalanter profile image
ygalanter

Wait till you see dynamic LINQ =)

Collapse
 
vekzdran profile image
Vedran Mandić

Or manually build expression trees haha :-)

Collapse
 
brianl profile image
Brian Lim

This is sexy

Now are any employers going to move to .NET Core 3 when .NET 5 is right around the corner and promises to unify everything? Probably not unless you're a business that rebuilds everything from scratch every 2 years. You'd be a fool to use .NET Core 3 if your core product gets rebuilt only every 5-10 years.

So yeah, we will see. Smart money is on waiting.

Collapse
 
grayblessing profile image
Gray

Mastering programming may seem like a daunting task, but with the right help, it becomes more attainable. anyknows.ai/ is a place where you can find various materials and courses to help you develop your programming skills. Thanks to this resource, you can expand your knowledge and become more confident in programming step by step.

Collapse
 
tebogo_sello profile image
Tebogo Sello

Thanks for the inspiration bro, 'cause I have just started with the language and I must say for a beginner it's frustrating with all it's .Net types😩😣😭 but after reading this for sure nami I'll be set👍

Collapse
 
eyasalmamoun profile image
eyasalmamoun

LINQ is your next thrill

Collapse
 
sduduzog profile image
Sdu • Edited

I never enjoyed anything like this in a long time. I just had to write about it. Flutter almost gave me a similar vibe but it wasn't that thrilling

EDIT: Jsx did!

Collapse
 
jreina profile image
Johnny Reina

I really enjoy your writing style. Followed!

Collapse
 
sduduzog profile image
Sdu

Thanks a lot, man. I appreciate that a lot. I try to make it as relatable and conversational as much as possible.

Collapse
 
hoodm194 profile image
hoodm194 • Edited

I have just started learning this programming language. And I don't understand everything about it yet, but it's still interesting. I also needed c++ assignment help, which is also difficult for me. And I am glad that there are platforms ready to help me understand it.

Collapse
 
ahmaddeel profile image
AhmadDeel

"Enter .NET Core 3! The most overwhelming piece of framework I've ever encountered"
to be honest I think .net core is not a framework

Collapse
 
hinasoftwareengineer profile image
Hina-softwareEngineer

I also hate C# because of so many libraries and many built-in methods in these libraries. But i enjoyed working on C# Windows Form Applications.