578 comments

[ 2.8 ms ] story [ 245 ms ] thread
I agree .NET is fantastic technology. I think a lot of the hesitancy in adopting it is from the historical churn described in the article. It’s normal for languages/runtimes to have churn in their features, performance, and library. .NET has that in spades, plus continual churn in its very reason for existence. The corporate and cultural forces behind .NET may be aligned with your situation right now, but five years from now, who knows? There’s not yet historical evidence that it will remain stable.
The switch from full fat .nets support life cycle to the current dot net cores totally confusing life cycle is currently pissing me off and the higher ups in my firm. Which should be worry for ms. I want stability , not breaking changes.
Yes, this. We will see how well support for the supposedly LTS DotNet 6 actually is long term...

I'm not a great fan of how the whole dotnet open source ecosystem has sped up to monthly and biweekly releases. Official Microsoft packages are some of the worst.

It's supported for 3 years after release. I think ut ought to be longer but it's really not that confusing: a release every year, and every other release is LTS (3 years support).
My one concern (as someone who is still supporting Framework code at the day job) is if they do major backwards compat breaks again. We still haven't decided our path forward to move our web code that uses various Libraries that were not ported.
Yeah agreed, the incredibly long term stability and backward compatibility was a real strength of the .NET framework.

But the industry as a whole has moved to fast releases and MS responded to keep up with the industry. This includes faster releases, and shorter support timeframes.

That’s the problem. They don’t have a steady design approach. They do something here, something there, then abandon the first, then go back to it. Desktop is a complete mess. Core looks quite good but who knows what they will do in a few years?
> (Note: there’s some debate on whether it’s 20 or 21 years, but Microsoft’s Professional Developers Conference in October, 2001 definitely already referenced .NET in production systems)

I can clear this one up.

I was lead on a .Net production system launched in August, 2001. It's 21 years.

Out of curiosity, did you also work on EMM386, as your username implies?
Now we just need commentary from a user named himem.sys and we’ll be all set.
Second this was writing production .NET in 2001, and betas were available in 2000. ASP.NET was originally named ASP Plus in the beta!
> Myth 2: It’s Slower than Node/Python/Go/Rust

This feels disingenuous. The author uses this heading for proof that ASP.net performs faster than similar frameworks in Node/Python. I could be convinced C# is faster than Go, but absolutely not for Rust. The simplicity modern C# provides (look at those code samples) comes at the cost of runtime performance optimizations.

Benchmarks would be required to prove it, but I could actually see .NET outperforming Rust in a few scenarios. First, C# can be written in a way that stack allocates almost everything (much like Rust) and second, the runtime information and JIT compilation may allow it to make better optimisations.
Only if you're including compile time in the benchmarks. Binary to binary, JIT is a great example of Rust having a compile-time optimization that .NET implements at runtime.
Well, some optimizations can only be implemented at runtime, so comparing Rust and C# without knowing the actual scenario doesn't seem to make much sense.
Optimizing compilers and JITs are difficult to compare directly: JITs have optimization pressures (e.g., selecting instructions/register slots as rapidly as possible) that AOT compilers don't (and vice versa), and (similarly) tend to have simpler IRs.

In general, even the best JIT is going to underperform an AOT optimizing compiler for those reasons. Microbenchmarks (and compiler quality) will always vary, but the macro picture is difficult to change.

I'm a big Rust fan but I fully expect that C# can beat Rust in some benchmarks and actually in some useful scenarios.

However, claiming outright that .Net outperforms Rust feels like an apple-to-petticoat comparison.

> the runtime information and JIT compilation may allow it to make better optimisations.

In practice this mostly means speculative devirtualization. This is absolutely true for C++, which tends to use virtual methods a lot, but Rust doesn't use virtual methods much. Moreover, profile-guided optimization (PGO), which Rust supports [1], allows the Rust compiler to take advantage of these speculative sorts of optimizations just as .NET does.

Granted, PGO is annoying to set up compared to the .NET VM, which "just works". PGO'd Rust is deployed in production, though.

[1]: https://doc.rust-lang.org/rustc/profile-guided-optimization....

I'm not sure about this specific case of Rust vs .NET, but the theoretical benefits of JITs never really seem to play out in practice, except for dynamic languages that can get really big improvements by using types speculatively.

I think that one of the things that JIT proponents seem to miss is that JIT compilers have practical limits to the scope of analyses and optimizations that can be done...quick, single pass analyses, basic inclining, etc.

An AOT isn't relying on competing with runtime resources to analyze and optimize code, and it can take a lot of time to do whatever it needs to. I've found that AOT with PGO is the performance holy grail for just about any use case.

There's one big benefit that JIT brings to the table that is not at all theoretical: it can devirtualize and inline across shared libraries. This doesn't matter if you link everything statically, of course, but that's not always desirable.

(Another benefit is that it allows for generic virtual methods, but that's only indirectly a perf thing.)

With .NET it's been really interesting watching the journey to greater and greater performance - there's been so many performance optimizations that weren't implemented for 1-2 decades. So .. watch this space, possibly JIT performance will get improved more when they run out of lower hanging fruit.
> C# can be written in a way that stack allocates almost everything (much like Rust)

I'd be surprised if this works in practice because the String processing in C# is surely going to keep making Heap objects ?

You're immediately in a better place here in Rust because people (sensibly) keep developing standards encoded in UTF-8 and so the bytes on the wire are Rust's built-in str type, whereas C# needs to turn them into UTF-16 and store that somewhere as a String.

> I'd be surprised if this works in practice because the String processing in C# is surely going to keep making Heap objects ?

The new Span<T>, specifically Span<char>, stuff allows you to slice and read strings without creating new objects, and string has both a c-tor for a (readonly) span and a Create method that takes a delegate that allows you to fill the string already allocated on the heap.

I didn't know about Span<T>, but it makes sense to give C# slices somehow. However so far as I've seen while writing C# popular APIs want an actual String and they won't take these slices as a substitute? The ergonomics seem poor.

Maybe I'm mistaken and magically my C# code which says "string" everywhere is actually converted at runtime to use Span<char> and go real fast? But it doesn't look like it.

The Rust APIs really do take the slice reference &str, it's just normal in Rust to say &str unless you very specifically want a mutable String for some reason (e.g. you're going to insert stuff into it)

I'm not sure if .NET can do this, but Java is similar, and it can do escape analysis to keep more things on the stack.
(comment deleted)
> First, C# can be written in a way that stack allocates almost everything (much like Rust)

While it works (and game developers using Unity tend to use that A LOT to get decent performances), it's also an enormous pain in the ass because you lose most of the “modern” features of C#.

JITs precisely can provide runtime performance optimizations.

What makes C#/Rust special? If you can't name anything in rust that would actually effect the dataflow analysis then you may need to reconsider.

Rust having one of the most sophisticated optimising compiler backends? And a whole suite of language design choices that enable fast, correct code?
The parent comment is clearly talking about language design rather than the backend.

I say this because I often see Rust people say isn't a great that rust can do xyz and then I look into it and it's actually just basic dataflow analysis that any compiler does or just LLVM being clever.

A JIT gives you the ability to target the current CPU better.
I'd say the primary factor is that C# has a garbage collector while Rust does not. C# and Rust are not really in the same league in my opinion. C# is more of a Java and Go competitor while Rust competes with C for the most part.
That's very dependant on the program being worked on.

Garbage collection can make certain programs harder but I promise you can write absolutely awful memory allocators in any language.

C# has some features that make it lower-level than Java or even Go. For example, it has pointers with pointer arithmetic, C-style unions, and explicit stack allocation of structs and arrays. In fact, the only two things in C that doesn't have a straightforward and efficient 1:1 translation to C# are signals and setjmp/longjmp (although the latter still maps very closely to exceptions).
> but absolutely not for Rust

Right. Here are measurement results based on the Are-we-fast-yet benchmark suite: https://github.com/rochus-keller/Oberon/blob/master/testcase.... The version of the benchmark translated to C and compiled with GCC or CLANG -O2 runs about twice as fast as the CLI/.NET version. Rust's performance is close to C's. And before you say that Mono is slower than the CoreCLR have a look at these measurements: https://www.quora.com/Is-the-Mono-CLR-really-slower-than-Cor.... But apparently bringing data is a waste of time.

>But apparently bringing data is a waste of time.

Frankly, bringing apple production data to an orange framers' convention is a waste of time...

I keep seeing you pop up in comments what feels like every time there is something about C# or dotnet or CoreCLR, again and again with your questionable comparisons and "data":

- You're compiling Oberon+ to CLR/IL code using your own compiler. Oberon+ is a language you yourself maintain it seems. It's not C# or F#, which are by far the dominant languages for dotnet. To me at least, Oberon+ seems like a rather esoteric niche language without wide adoption, but I could be missing something.

- The quality of the codegen your Oberon+-to-CLR/IL compiler manages to achieve compared to the dotnet compiler that people would actually use most of the time, that's a big unknown. But I see a real chance that the dotnet compiler, that saw countless hours of development time from a large team of some of the brightest domain experts that MS could find, is outperforming your compiler's codegen.

- How well Oberon+ translates to CLR/IL (or CIL, basically bytecode for compatible .NET runtimes) is another unknown (is it straight forward, does it require the compiler to add a lot of "glue" code or structures to accommodate that language?)

- You're not running your benchmarks on the dotnet CoreCLR, but on mono. And you're trying to handwave this important fact away by linking one of your own quora posts that again uses your own Oberon+-to-CLR/IL-with-my-own-compiler benchmarks that allege to show that there isn't much of a difference between mono and the CoreCLR runtimes. Maybe there isn't, or maybe there isn't just for the very narrow specific IL your Oberon+ compiler produces for the Oberon+ benchmark code you use. But even looking at your own results in your quora post, there seems to be a significant difference in performance even with your narrow use case when comparing mono to CoreCLR 5/6 performance, in favor of the CoreCLR.

- You're running the result of what your own Oberon+-to-CLR/IL compiler spits out on mono-3 (last release 2015, EOL) and mono-5 (last release 2019, EOL) runtimes. Current stable mono-6 was released in 2019. So you're not even running on a current mono release, let alone a current CoreCLR release. The old (mono) runtimes and standard libraries lack a lot of performance-related features that modern CoreCLR versions have (especially when it comes to value types, Span<> and stackalloc, etc, that can be used explicitly in code or in a lot of cases even implicitly by an optimizing compiler)

- You're running on rather old hardware, and a laptop hardware no less. The Intel Core Duo L9400 you mention was released in Q1'06, but according to my google-fu the HP EliteBook 2530p actually uses the SL9400 (is that a typo in your benchmark results pdf?), which was released in Q3'08. It still only comes with DDR2. Would be interesting to know what storage option yours has (as cold-start times of your benchmarks might be dominated e.g. by a slow 5400 rpm laptop grade HDD that is one of the 2530p's storage options). And you run benchmarks on a 32 bit OS. All of which is fine, if you're interested in results on such hardware, but I'd think most people would be more interested in results on slightly more modern hardware.

- You managed this time to comment on a Rust-related post, agreeing that Rust is probably faster, trying to justify this assertion with a set of benchmarks that do not include Rust whatsoever, just saying that Rust performance might be close to C. Which is not a bad hypothesis to start with, but when you go on linking your data and nonchalantly proclaiming that "bringing data is a waste of time" when you didn't bring any data regarding Rust, then I don't get your point.

At least you had a look at the links, but obviously not good enough; your reproaches are unnecessary and unjustified. It is irrelevant that the benchmark is written in Oberon. Much more important is the fact that it is not just one of those non-representative micro benchmarks, and that the same code translates fairly directly into both CLR/IL and C. So we can directly compare the ability of CLR to optimize this code with the optimizations done by GCC or CLANG. The fact that Mono is only a factor of two behind is pretty good. But at least it's a factor of two. That Rust's performance is very close to C's is well known (from CLBG and other benchmarks); you're invited to bring your own version of the Are-we-fast-yet suite in Rust otherwise. You scatter doubts that my data is unrepresentative by simply making some rather adventurous claims whose credibility is not supported by any measurements or data at all. It is irrelevant that C# is the most commonly used language on .NET; it is by definition and design a "common language infrastructure", and the article comments on .NET in general (and makes claims at least as adventurous as yours, the ones discussed here also without supporting evidence). Regarding your nonsensical claims about Span<> and stackalloc, I recommend that you take a look at ECMA-335 and its development over time (and at the generated code). Also your assertion about the relevance of the HDD and the other mentioned hardware aspects makes me doubt whether you have any idea at all about what you are writing here; but in this day and age, that seems to be enough; so why should people take the effort of making measurements and publishing data?
> It is irrelevant that the benchmark is written in Oberon.

It is not irrelevant from my perspective. It might be mostly irrelevant if Oberon, as you say, "fairly directly" translates to CRL/IL, that is, there is no need to introduce any (inefficient) glue constructs to support Oberon features. I didn't/do not know this, but if you say this to be the case now, good, that answers my doubts in that regard.

However, this still does not speak to how good the codegen of your Oberon-to-CLR/IL compiler is. Yes, the mono or CoreCLR JIT will apply further optimizations, but in a significant part it relies on the quality of the codegen of the IL it gets passed to do so.

>The fact that Mono is only a factor of two behind is pretty good.

According to your benchmarks. I'd still be a lot more interested in how well the CoreCRL would fare when handed CLR/IL generated from idiomatic C# or F# by the dotnet (Roslyn) compiler. On recent hardware. Or at least a recent hardware architecture.

Right now, it seems to me you're doing something analogous to compiling C with tcc and then making general performance statements about C performance based on tcc generated machine code. Even your own mono-3 and mono-5 results differ, so choice of runtime is clearly a factor.

>It is irrelevant that C# is the most commonly used language on .NET; it is by definition and design a "common language infrastructure", and the article comments on .NET in general (and makes claims at least as adventurous as yours, the ones discussed here also without supporting evidence).

It is not irrelevant. The claims unsupported by any evidence are yours by the way. You made statements about performance, and all you presented is what I consider at best highly questionable methodology. If you make such claims, and try to back them up by "data", you'll have to demonstrate that your Oberon+-benchmark-code-compiled-to-CLR/IR-as-run-on-old-mono-on-outdated-hardware benchmark methodology is indeed something that can be used to make such claims about ".NET" in a sound manner. As I said, I doubt that, and that's all I did, express doubts about your methodology, and why I have these doubts.

> Regarding your nonsensical claims about Span<> and stackalloc, I recommend that you take a look at ECMA-335 and its development over time (and at the generated code).

I have been following standard library and CLR/IR development, and Rosyln compiler development, and therefore know that my statements are not nonsensical. E.g. here[0] is the dotnet "Augments" document that lists, aside from many clarifications, outright additions not present in the latest ECMA-335 spec. See e.g. the changes/additions regarding Byref-like types, which in practice allow for all kinds of optimizations, a lot of them implemented in CoreCLR 5 or 6, but of course only enabled if the compiler's codegen emits these new byref-like type variants.

Furthermore, a lot of the optimizations that current-generation to-CLR/IL compilers and runtimes/JITs perform do not rely on changes to ECMA-335 at all. E.g. as I mentioned above, your mono-3 runs the CLR/IL your compiler generated significantly faster than your mono-5.

>Also your assertion about the relevance of the HDD and the other mentioned hardware aspects makes me doubt whether you have any idea at all about what you are writing here

You're running on a somewhat memory-constrained old machine, so even if you try to run your benchmarks "hot", i.e. have everything required loaded into OS-level caches before the timing starts, I wouldn't be convinced that this is necessarily the case (disk cache pages might have been evicted, and pages in general might have been swapped out and need swapping back in again during a run, and if that happens the HDD performance would play a major role). Additionally, laptops (with relatively early versions SpeedStep-capable CPUs, as your is) are par...

I have worked with .NET for a long time and recently did some work with Java, node and python. .NET has a lot going for it but i feel MS is making things more complicated than they have to be. When I need to achieve something a little off the beaten path in the non .NET technologies this is often pretty straightforward to achieve whereas in .NET I have to find some obscure interface and overrides to implement and then deal with weird side effects. I think .NET has become too big for its own good to be maintained by one party. The documentation also has a lot of volume but almost no structure. When .NET started out it was pretty coherent but in the last few years it felt very disjointed without a central design philosophy. Don’t even mention desktop development which is a complete mess since Windows 8.

Maybe I am too negative on .NET but personally find the open source alternatives easier to work with and more fun.

The .NET frameworks (blazor, etc) are too complicated. Peak FizzBuzzEnterpriseEdition there.

The interop layer than you have to go through to do a lot of stuff is actually quite tame. It just needs more time on Linux.

>The interop layer than you have to go through to do a lot of stuff is actually quite tame. It just needs more time on Linux.

What do you mean?

I have software on prod on Linux for years and the only problem I had was lack of some fonts installed that made some problem when it comes to pdf generation

Binding to C/C++ libraries or other system libraries is not something that's well documented, officially. On Windows there's a lot of 3rd party effort that is easily searchable. But for a long time the interop layer was not (and still may not be?) working on Linux.

The first iterations of .NET were just for webapps. You could only work with and call .NET code, the only functions provided were basic IO and network.

Hmm, I've been using .NET since beta 1 in 2000. Maybe you had access to earlier bits? It was never just about webapps. In fact, Windows Forms was meant as a more sane MFC if anything.

Interop with native code was always very easy. From PInvoke to managed C++ there were a lot of ways to do it with ease. Things have changed but I still think this is one area where .NET has great support.

They probably mean .NET Core, it's somewhat true in that case.
Native interop in .NET has always been via what they call "P/Invoke". On .NET Core, P/Invoke is cross-platform and you can also call .so/.dylib functions on Linux/MacOS just like you could always call .dll functions on Windows on .NET Framework before we had .NET Core.

I'm curious to know what you needed to do with native libraries. I've been programming with .NET around 20 yrs and find it fairly rare that I need to use P/Invoke... But I'm sure it depends on what you're building.

I maintain a thingy written in C# that does cool things with keyboard backlights on System76 machines in Linux. Like make the left-third respond to CPU usage, right third blink when you have unread notifications, etc. The biggest one was turning off the keyboard when the screen blanked[1]. Almost all of the fun stuff requires native interop.

1: https://withinboredom.info/blog/2019/11/26/detecting-if-a-sc...

You end up using P/Invoke any time you need to control hardware. In the last few months I needed to use P/Invoke to control cameras, motors, and compute FFTs with the GPU.

You need to use them if you need to control raw GDI objects like bitmaps.

That's the normal Java culture, which is not surprising given that MS created .NET as a competitor to Java.
> The .NET frameworks (blazor, etc) are too complicated. Peak FizzBuzzEnterpriseEdition there.

I dunno, Blazor feels like the one ASP framework that makes sense. MVC, Web Apps, Web Forms all feel impenetrable to me.

Blazor IMV is more sane than React. It is an SPA that is structured around routing and pages, which happen to have component hierarchies in them.

React is the other way around. Routing in React seems to be an afterthought. And I hate it with a passion.

Page based way of thinking (or "screens" if you will) is a better way to do web dev.

Also, the Dependency Injection system is extremely productive. State management in Blazor is very easy and a breeze to use.

If not for the intricacies of hosting models, and a slightly large default build size for the full WASM client mode, Blazor would have replaced React / Angular / Vue by now.

> Dependency Injection system is extremely productive

Sure, but there is a downside to it. You want to add a feature to the class that uses DI? Great, just add an injected object to the constructor. Except now a 100 unit tests broke because you changed the constructor. A 10 minute functionality change turns into a massive cut and paste operation in your Tests project.

Sorry, if tests are not adaptable to changing code, why are they called tests?

I thought tesring was to check software,not be a constraint on software development.

Also, I think using DI in the test process itself will mitigate the issue.

Some tests are not adaptable to changing code because of the way MS uses DI. A lot of DI is done by adding new parameters to the constructor.

It’s the same with async. Needing an async function somewhere can result in a huge cascade of changes.

Constructor inject is generally a positive here, though, as the compiler will tell you where you need to add the new dependency. If you find that you have to add the dependency in lots of places just to satisfy the compiler, not because the functionality actually requires the dependency, that's usually a sign the class should be broken up into classes with more clearly defined responsibilities.
“If you find that you have to add the dependency in lots of places just to satisfy the compiler, not because the functionality actually requires the dependency, that's usually a sign the class should be broken up into classes with more clearly defined responsibilities.”

Sure in theory this is correct but you can’t always anticipate future needs. The class may still have very clearly defined responsibility but it just needs another piece of information that you can get only with DI. I feel this over dependence on DI in .NET forces you to design around the framework and not around what makes sense.

(comment deleted)
I solved this with a test helper method that creates a fully mocked instance and returns a struct with the mocked instance and all the mocks that it consumes. Unit test setup starts by getting a new instance and overriding the mocks that it cares about. Now constructor breakage is isolated to fixing the default mocks in the helper method.

Maybe I should write this up somewhere.

I did that too and it should be the default. Constructor injection is a bad pattern.
What do you mean by "constructor injection"?
In .NET a lot of DI is done by adding another interface to the constructor. So if you need some service way down the stack you add it to the constructor. This then in turn can require adding this dependency to all the tests that use this service.
My comment describes a way to organize unit tests to work around this issue.
Comments like these really amaze me. What sort of competent editor cannot handle such simple refactorings?
Maybe but in Blazor you have the choice of Property or Constructor DI. This choice is in all the major DI container libraries I've worked with in .NET, around a dozen over the years.
>Maybe I should write this up somewhere.

Please do, as it sounds interesting.

We use a tool called AutoFixture to automatically generate objects being tested. It will automatically mock constructor dependencies, so a change like you described doesn't require any test changes unless the new mock requires some setup for the test to pass.
Compared to JS SPAs such as React/Vue/Angular, Blazor obviates a huge amount of toolchain complexity, multi-language and transpilation impedance, and jank of leaky abstractions building a component model on top of Javascript.

Having worked with a mix of these, I found Blazor to be clearly much simpler, approaching though not quite reaching the simplest possible implementation for a component based UI.

I think it's probably more a matter of what you are used to than inherent complexity. The things you are familiar with seem 'easy' and the unknown seems 'hard'.

Stick with it, it really pays dividends when you get good at it!

(comment deleted)
I find this very intriguing. As a runtime, .NET exposes a lot of high and low level stuff to the developer.

Can you give me an example of this offbeat thing you wanted to do that involves overriding an obscure interface?

.NET is big, but if you are using it for web app, your focus is only on that set of libraries. Comparing with similar stacks, none of them cover the area .NET covers.

Also, look at python. People don't see it as big because it does not have an IDE that asks you to select projects and what not, making it seem like it is a tool small enough for their work. However, if you combine every library or framework that covers what .NET does in Python (Web, Desktop, Gaming, System, ML) you will find Python is also just as large.

I have the same experience as the parent comment. For example, I wanted to have a process that runs both the asp.net service, a background discord bot, and another asp.net service.

On any other platform, it's just a good old "console app" equivalent, and you can start multiple services in your main function, catch SIGTERM, and control each service on your own.

On .NET, the project type has to be some weird Microsoft.Web.SDK things if you wanna run asp.net, and the IDE/build system would figure out what dependencies to build. And if I wanna run other services in the same binary, tough luck.

Why can't ASP.NET just be constructed as a regular console app, where you pull in nuget dependencies, instead of a completely different app type, for example.

(comment deleted)
Huh? I don't think you read what I'm saying.

Here's a simple challenge for you. Create a command line app (your csproj file should say Sdk="Microsoft.NET.Sdk"), ask the user to input a port number at runtime. Use this port number to start an ASP.NET service.

I have not had any luck to pull all the right nuget dependencies to do that so far. The only way to run asp.net app I could find is switching your whole project to be Sdk="Microsoft.NET.Sdk.Web"

You can run multiple asp.net core services on different ports on same machine. You could create an over-arching console application to automate the same, or you can do it with a simple script.

https://docs.microsoft.com/en-us/troubleshoot/developer/weba...

Nope. Not the same thing. Again, I want them to be in the same binary. Like the example I gave you, have the user input a port number *at runtime*, then use that port to start an asp.net service.

That's just an example. The point is mixing a regular console app with an asp.net service.

1. Create a new web project (it is actually a console app)

2. In program.cs add the following lines at the top:

    Console.Write("Port number:");
    var port = int.Parse(Console.ReadLine());
3. Change the bottom line App.Run()` to

    app.Run($"https://localhost:{port}");
You now have an application which asks for a port and launches your empty website on that port.

A `Microsoft.NET.Sdk.Web` project is still a console app. The "web" variant has some other default packages and (crucially) it has some other defaults for how to compile files such as cshtml.

I can confidently tell you that what you put here won't work. Try it.
I did. It works. The above code lines are cut directly from a test project that I made.

Several people have pointed out how a web app in .NET 5/6 is really a console app which only starts a web server configured with an application and waits for it to complete (i.e. shut down).

It really is that simple.

The whole point here is that the project does not target the Sdk.Web thing. Yours only works because the project is targeting Sdk.Web (instead of Sdk).

When I said console app, I literally meant using the project that you would create if you do `dotnet new console`.

To copy from my other comment:

That is exactly the thing. I do not want to have the Sdk=...Web/Worker. Imagine this scenario, you started a new project with the Sdk targeting Worker. Then you need that binary to also target web. What do you do?

- If you switch the project to Sdk=...Web, you won't have the dependencies to build the worker services.

- If you keep it as Sdk=...Worker, you won't have the dependencies to build asp.net

The "Sdk.Web thing" is the way the project is built.

As I and several people in this thread has pointed out to you, the application the resulting application is not a "web" application. It is a console app that launches a webserver.

The Sdk (without web) is not designed to compile cshtml and/or razor source files. It has nothing to do with package dependencies.

If you do want to start of with a console app sdk, then look at "minimal API". You simply add package references to Microsoft.AspNetCore and Microsoft.AspNetCore.App to your console app, and then you can start using the minimal API samples. They will also launch a website.

The example I showed you above was literally adding two lines and changing one line; and then you had a console app which asked the port and launched a webserver on that port.

If you start from a console app, you will not have build targets configured for cdhtml and razor files. However, to prove that you can launch a webserver the same way you can do this (yes I have tried it and it works):

1) Add dependencies to your "console sdk" project:

    <PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.8" />
2) Replace program.cs with this (this is the entire app):

    using Microsoft.AspNetCore.Builder;
    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    app.MapGet("/", () => "Hello World!");
    app.Run();
Voila. A web server.
Here you go:

  <Project Sdk="Microsoft.NET.Sdk">
  
    <PropertyGroup>
      <TargetFramework>net6.0</TargetFramework>
      <Nullable>enable</Nullable>
      <ImplicitUsings>enable</ImplicitUsings>
      <OutputType>exe</OutputType>
    </PropertyGroup>
  
    <ItemGroup>
      <FrameworkReference Include="Microsoft.AspNetCore.App" />
    </ItemGroup>
  
  </Project>
Just create an empty ASP.NET Core project, change the SDK, add a FrameworkReference and a OutputType.

Read the console args and configure the web app with a port as desired.

Thanks! This is what I was looking for (not anymore, sorry switched to a different language/ecosystem for this project).

This still begs the question, or my original point still stands—why framework reference vs. nuget reference? I'm sure there's a perfectly rationale explanation, but this in itself, having at least two "official" ways of managing dependencies (framework reference vs. package reference on nuget), it's kind of a "code smell" imo.

Because ASP.NET Core is a framework that is installed with .NET. It isn’t a set of Nuget packages.

Why? .NET Core 1.x tried making everything a Nuget package and it was a disaster. ASP.NET is big which meant a huge number of dependencies, easy to create conflicts, long restore and build times.

It was much better to just include ASP.NET Core in the box with .NET and simplify it down to a single reference. And most people use the web SDK which does it for you so they don't even need that.

What about the option to just make everything asp.net one single nuget package? And how come other ecosystems/languages don't have this issue? What's the difference?

The other question is, why isn't what you posted the default, but defaulting to this <Project Sdk=...>? To me, it's a lot more intuitive, signaling that each project can have multiple framework references, vs. just one SDK attribute.

You're searching for ways to make things more difficult than they need to be.

Make the simple easy. Make the complex possible.

Disagree here. This is where I would quote the principles of two other language.

From Python: have one way of doing things

From Go: explicit is bette than implicit.

Is Sdk=... is just a short cut to pull in a handful of framework references? Or are there actually more things happening under the hood?

https://news.ycombinator.com/item?id=30673710

sigh I can't help it...

Python really isn't a great choice for making your case. "Have one obvious way of doing things" has become an ironic meme since forever, and toolchaining and dependency management has traditionally been a hot mess (yes, even with pip and virtualenv, just not the total clusterfudge it was before. And don't get me started on pipenv.), and only gotten better recently with 3rd party tools like poetry, or even conda.

>What about the option to just make everything asp.net one single nuget package? And how come other ecosystems/languages don't have this issue? What's the difference?

It's no different to e.g. create-react-app vs npm install react.

(comment deleted)
> I have not had any luck to pull all the right nuget dependencies to do that so far.

thats because they are not directly on nuget. at least not in the included one, you need a framework reference, since for packaging reasons they have a runtime and a "web runtime" (kinda like that)

https://docs.microsoft.com/de-de/aspnet/core/fundamentals/ta...

ASP.NET Core apps are actually straightforward console applications. In fact most of the application types you’ll run on .net core are configured in a main method in program.cs. The answer to your specific questions about mixing different services in one app will be some combination of the following links.

https://khalidabuhakmeh.com/hosting-two-aspnet-core-apps-in-...

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/ho...

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/ho...

That’s the problem. You have to read multiple articles to achieve simple things.
Well how would you do it if another platform if you are a beginner. Because I am a beginner in Rust, this is the process I would use:

1. search Google, read few articles or posts

2. write code

3. make it compile

4. encounter bugs

5. research the bugs on yet other articles or posts

6. fix the bugs

7. ???

8. profit!

It's not about if you should or should not search. It's about how often you have to do that, and how easy it is to search for what you want. It boils down to how much anti pattern a framework/ecosystem has. In this case, their default template caused me to search "dotnet core project multiple SDK", which yielded nothing, and is actually a completely wrong track.

The answer the asp.net team shared below, was instead of using the SDK attribute, use a completely different thing called FrameworkReference. Which can completely replace this SDK attribute, it seems.

Hence my question to them below was, why is framework reference not the default? Especially since it does lead to better searchability, and the template shows that one could have multiple of these per project, intuitively.

The original poster is going slightly off the beaten path and therefore has to manually compose three or four different concepts together to come to the right solution vs relying on the out of the box experience.

I, and a lot of other devs, would be able to solve this particular problem without looking up the docs but I can’t assume any knowledge on the poster’s behalf so I posted the links to the docs about the building blocks and an article showing one possible way of composing them.

The same exact problem as posed by the poster was thought of by the dotnet/aspnet teams and the pieces (apis/docs/samples) are all there, just not the default.

> I, and a lot of other devs, would be able to solve this particular problem without looking up the docs

Look through the rest of this comment thread and see how many failed attempts at solving this problem there were before two high profile members of the ASP.NET team came in (JamesNK and davidfowl).

It's not about looking up or not looking up. The criticism here is that the way the framework is laid out is not intuitive enough. It's very "different" from the rest of the industry (in this case, there are 3 potential ways of pulling in dependencies for ASP.NET). This requires a lot of time investment for its users to solve these slight one-off issues.

My question to them below was why isn't what they shared here the default. If they had done that, it would be intuitive to know that one can add other "FrameworkReferences" in a project, or know what to search for. Instead, the default is "each project can only target one SDK".

JamesNK also answered this. We started off with just nuget packages, it was beautiful for about 5 minutes until we ended up with ~300 in the default project. Then physics kicked in, slower build times, slower compilation times, slower intellisense. All of those old O(N/N^2) algorithms started to show up on profiles and we had to do something about it. That was just the practical performance side of things, then there was the customer confusion around which packages had which APIs. We offered a .NET buffet that customers hated. On top of that, the versioning got nuts. Each of those packages could in theory version independently, who is going to test all of those combinations of things? What happens when you need tot publish ~300 + packages to your server deployment because you didn't want to "install the framework"? You'd be complaining that they were too many assemblies (which people did). Amplify that by deploying these binaries to the same physical machine when running multiple .NET Core applications there (very popular for IIS setups). We pre-JIT (ready to run) the core libraries and ASP.NET to improve startup time, that makes the assemblies bigger (as they contain both native code and IL), this makes your applications bigger by default.

We got LOTS of feedback that this was all really terrible and we listened.

We did this from .NET Core's inception to .NET Core 3.0 when we pulled the plug. We set things up so that the base install/platform/framework was not composed of packages but framework references. We merged several assemblies together to get rid of some of the unnecessary layering. We invented shared frameworks so that people could install the framework once and run lots of applications using shared libraries so that:

- Customers have faster publish times as you only need to deploy your application bits, the framework can be pre-installed - Loading the same dll on disk into multiple processes allows for more virtual memory sharing (a handy performance optimization) - We could version the set (.NET, ASP.NET Core) as a coherent unit - We could pre-JIT (R2R) the built in stuff so it's installed on the machine once and usable by many apps.

As for being intuitive, the default experience is to use the Web SDK. I didn't even get into SDKs but it does more than default the framework reference. It also exposes capabilities that tooling use to light up behaviors in the build and in the IDE.

PS: This stuff is harder than it looks on the surface and we spend lots of time and take lots of care designing it (making the typical tradeoffs you make when doing software engineering).

(comment deleted)
@David, thanks, and completely aligned on why we don't use nuget packages anymore.

> As for being intuitive, the default experience is to use the Web SDK. I didn't even get into SDKs but it does more than default the framework reference. It also exposes capabilities that tooling use to light up behaviors in the build and in the IDE.

It is these "does more than default framework reference" are precisely the things in discussion here. All of these hidden functionalities and dependencies are like a black box. What's the difference between what James shared here and the default Sdk=...Web, then? It's the lack of uniformity, where "ASP.NET is a first class citizen" rather than just another piece of the ecosystem that is a turn off.

Compared to other ecosystems, for example: Go, Rust, even Java for example, where everything is just code that one can pull in, and the customization is in the code, not the runtime/JVM.

That is exactly the thing. I *do not* want to have the Sdk=...Web/Worker. Imagine this scenario, you started a new project with the Sdk targeting Worker. Then you need that binary to also target web. What do you do?

- If you switch the project to Sdk=...Web, you won't have the dependencies to build the worker services.

- If you keep it as Sdk=...Worker, you won't have the dependencies to build asp.net

But those are just sets of convenient defaults? You could just start with the default sdk and add the imports/targets yourself (or in practice: let the IDE do it for you.)

And

>- If you switch the project to Sdk=...Web, you won't have the dependencies to build the worker services.

Yes you will, since the worker sdk is simply a subset of the web sdk.

> You could just start with the default sdk and add the imports/targets yourself (or in practice: let the IDE do it for you.)

That's what I have been calling out. I have not had much luck in pulling in the right dependencies needed. Could not find any documentation on it. Everything relies on that Sdk=...Web thing on the official documentation.

If you’re just looking to be unblocked, use the Web SDK because you’re building and running combined web + hosted services [1]. The section linked shows the precise difference between the Web and Worker SDKs for hosted services.

If you’re looking for docs on what all the SDKs are/do, and their source code, start here [2].

If you’re looking to just add the asp net core packages/apis to your basic sdk/console project, here [3]. But note that your app build/publish probably won’t work 100% because msbuild won’t be configured to do so.

If you want to fix that manually (i.e do what the Web SDK does automatically), You can use the Web SDK props file as a starting point [4]. (Linked to in [2])

It seems well documented to me, but you’re welcome to propose new docs to the aspnet docs team. They’re very responsive [5].

[1] https://docs.microsoft.com/en-us/aspnet/core/fundamentals/ho...

[2] https://docs.microsoft.com/en-us/dotnet/core/project-sdk/ove...

[3] https://docs.microsoft.com/en-us/aspnet/core/fundamentals/ta...

[4] https://github.com/dotnet/sdk/blob/ee98c8c25188195fd4f8a145b...

[5] https://github.com/dotnet/AspNetCore.Docs

Well, not much to add to what jkulubya said, but: Just use Web, then - that's what it's for and it just worksTM.

We have almost your exact scenario in production - a (Quartz.NET-based) service for background/scheduled jobs, with a web dashboard and a healthcheck API, all from a single console executable without external dependencies. And it was super straightforward to implement.

I agree with your sentiment. It's probably a legacy left-over from the .NET Framework days where you had to choose upfront what app you are going to build and where it was going to run and then you had the full IDE experience configured. .NET team has made a lot of right steps to fix this, however they haven't fixed MSBuild. As a long-term .NET developer (not anymore sadly, but still find it the best development experience) I always found MSBuild confusing but it has only become harder to learn because it has too much going on under the hood.

So, if you want to do what you are saying, you should look into learning more MSBuild because .NET certainly allows you to do that without workarounds if you have your build configured correctly.

I think C# is incredibly close to Java in this regard.

Not in the specifics of e.g. your problem, but the big "solutions" tend to be very enterprise solution-y, single-goal orientated. Spring in JAVA (also J2EE) had a similar sort of issue, where everything worked as long as you weren't veering off of the beaten path, but things got wildly over-complicated as soon as you dared to stray...

The claims made in this article also sound a lot like what Java folk would say back when their platform wasn't yet "cool" - they are nevertheless somewhat successful today, so maybe they have a point?

Regardless, you can absolutely do web dev from a simple C# console app, I don't know there are pre-build server frameworks for doing so. If you don't like MS's offering, tough.

They did that around .NET Core 1.x, but it became unwieldy (trust me it was a complete mess) and they switched it for the framework references, and they switched that for the SDK included dependencies iirc.
I can definitely see the appeal of a simplified .NET lacking a lot of the baggage it has from 21 years. .NET core was an attempt at stripping a lot of stuff out but over time they've had to add stuff back because it turns out telling developers they have to rewrite all their code (or give up on doing things at all) is a bad idea.

Node benefits from being a newer ecosystem (~2009) that started out with simpler goals, so it accumulated cruft more slowly. Ecosystems like node also deploy breaking changes more often - .NET 1.0 software can still be run on a modern Windows machine using the latest regular framework, though as of 5.0 they finally decided to kill existing .NET code (the old framework still works, 5.0 and later just use a separate one).

I'm personally still writing and maintaining .NET 4.x code and some of it is working stuff I wrote in ~2005 and it has been serving me well that whole time. I have extensive experience with C++, JS, Python etc and C# is still my first choice when it comes time to solve problems (Python is a somewhat distant but still respectable 2nd place - the REPL is great.)

From time to time I've been able to solve difficult problems with built-in .NET tooling - I wrote a custom compiler for a DSL from scratch entirely using built-in 4.x features and it can do hot reloading, generate debug information, allow me to set breakpoints in visual studio, etc. I simply can't get this anywhere else.

.NET is an amazing tech built by MS. I wish more people would use .NET ecosystem instead of Go, which is pushed by Google so Google can make more money.
How much does Google charge for Go?
I disagree with the point that Go is a big money maker for Google. I assume the implication is that Go helps make sales for Google Cloud the same way .Net acceptance leads to Azure sales. That said, in my experience this isn't true. Some of the biggest adopters of Go (Uber, Netflix, Twitch) are AWS based. The real profitable part of Go for Google is the scalability and efficiency of the language for Google projects but that's not a very effective argument to switch away from Go.
.NET exists because it is used by Microsoft to make Microsoft more money. That's literally what a platform play is. What is bad about Google doing this with Go?
I have used both from day one. I prefer Go. 900% less faffery with it.
> .NET is an amazing tech built by MS. I wish more people would use .NET ecosystem instead of Go...

After years of witnessing or being on the receiving end of the things Microsoft did, I'd rather not develop with .NET. Same goes for Google's Go. Rather I'd happily use C++ / Python / Rust / Java / Perl, and never look back.

I think it's bitter to be associated with acts done to undermine and stifle competition for whatever it takes rather than being a tech company known with good products. Yes, I pay for an Office license and like their hardware, but I can't forget what they've done over the years.

How is Google making money off of golang?

Are they selling tools for it? Not that I see.

Are they selling the compiler? Nope (they don't even 'own' it, it's open source)

You really need to explain your thought process here because it doesn't sound based in reality.

Go and .Net languages aren't in the same space unless you can build native static binaries with .Net tools.

Unless and until the "installation strategy" for a .Net program is "copy the binary to the target system" they aren't going for the same kinds of people. That's OK, diversity is good, but don't pretend the differences don't matter.

This is pretty much the case with .net now. I build zero install systems that carry the appropriate framework with them.
Does the target system need a CLR interpreter already installed?
No, it gets dumped in the directory when you build a stand alone system. Ive used this where I am deploying things for customers who don't want to install extra stuff on their servers as an example.

Super handy for customers who insist on running ancient versions of windows server too.

No. You can also build a single file like in go. Its set to be even better on .NET 7
Native static binaries are such a small part of what defines Go. Go is absolutely a competitor in the jvm/.net space. C# and Java are its closest competitors since they're all performant, statically typed, and garbage collected.

Compiling to binaries is an advantage but I can guarantee it's not why most people use Go.

I wrote a little command line tool in C# a couple of years ago. I'm lazy, I just copy it into the working directory and run it there.
Single file deployment has already existed in the last version (.net 5.0), is better in the current version (.net 6) and set to get even better in the upcoming releases.

Also the rise of containers has obviated much of the issues around deployment and I've never seen "native static binaries" be a requirement for choosing a language stack.

.NET just seems a lot more full featured and batteries included. I haven't used either, but .NET looks pretty fun to develop with.

I think I'd still take JS over either though.

> Myth 2: It’s Slower than Node/Python/Go/Rust

I guess apples-to-apples benchmarks are so hard to agree on that any statement of the form "A is slower than B" is always to some degree a myth. If that's the author's position, fair enough. But if we actually want to make comparisons, "C# is faster than Rust" is a bold claim.

...especially when in two out of the author's three screen captures, C# is actually shown as slower than Rust.

I didn't spent time to investigate further because I feel that this headline is not really useful in the first place.

The reason why it's a myth worth dispelling now is that The (legacy) .NET Framework, which used to be ".NET" back in the day, actually was hilariously slow for any kind of web or networking work. It had decent CPU crunching power, but also only on Windows machines.

Comparisons of benchmarks with C# vs. Rust or whatever are unproductive and it's a shame that the article focuses on that. The reality is that you can write C# or F# code today, focus on solving your problem instead of performance, and have it handle a production workload admirably well. That used to not be true circa ~2010. Thankfully it is true now :)

This is my experience with dotnet. Considerable amount of legacy code coupled with different web frameworks along the way. While the app was slow, I found the amount of time it took to make changes to be the unbearable part, mostly a whack a mole situation. Maybe I worked with some bad developers, but they all acted as if dotnet was the best thing ever with constant mentions of dependency injection and blaming the GAC for problems they clearly created..
All ecosystems have churn. While .NET has had a fair share of churn, to me it seems in line with any other platform that has not only survived but thrived for this long.
Most of the problems I've seen of this sort are from easy-to-make design issues, like constantly reassigning a string value to modify it (lots of allocations, with lots of GC), or using an IEnumerable with .count() then iterating with foreach (causes multiple enumerations and maybe loads), or unnecessary conversions between List and arrays.

If you don't understand what's happening underneath, these mistakes are easy to make. The worst part is these compound over time, and also aren't obvious at small scale.

This isn't a problem unique to .net though.

I disagree. It was pretty good even back in 2005. There were a couple of very large high traffic sites I ran on it back then.

I wouldn’t use it now because the big cost is always humans in a project and anything .Net turns into a damage amplifier because of the amount of fettling and dealing with churn you have to do to get anything done. Even with .net 6.

Agreed.

I had a web app in 2006 handling 20k RPS on three dual core/2GB servers without breaking a sweat. This included per-server connection pooling to MSSQL and lots of UDP traffic on top of memcached. It was rock solid.

The .NET Framework HttpClient was considered really slow by the .NET team themselves. In .NET Core they scrapped the old code entirely so they could build a good one.
StackOverflow was founded in 2008 and probably had plenty of "production load" circa 2010.
StackOverflow is also a classic example of an engineering system that has focused on performance since the very beginning.
I believe StackOverflow has been running with .NET since they started in 2008, and they were pretty surprised by the scalability and performance. They do use Windows servers though. They've been reasonably open about sharing their architecture.
That reminds me of all the "Java is not slow" articles that came out back when Java was more popular.

If it really wasn't slow, why would they have to defend it? We don't see "C is not slow" articles.

I don't know whether modern Java is slow or fast compared to other languages since it tends to be fast enough for many applications these days. Yet Java earned a reputation for being slow in the early days, may that be due to the startup time of the JVM when Java applets were embedded into web pages or the lacklustre performance of overly ambitious applications built upon a (then) immature platform. Even though those issues were dealt with in one form or another (e.g. applets dying off as well as the development and improvement of the JIT compiler), the reputation has been hard to shake. I suspect that much of that is due to a "fool me once, shame on me; fool me twice, shame on you" type of attitude. In other words, people haven't bothered to see if it has improved since early Java did not live up to the early hype.
> If it really wasn't slow, why would they have to defend it? We don't see "C is not slow" articles.

Because there’s a justifiable concern from some about a bytecode system being slower than raw machine code. After all, bytecode requires, at a minimum, interpretation to run. And with JIT systems, there’s still a cost associated with it.

You don’t see “C is not slow” because no one is arguing that it is.[a] There’s nothing faster than machine code[b].

[a]: Maybe back in the day when assembly was still king? If the internet was as prevalent 20 years ago as it is now, I’m sure you’d’ve seen articles arguing “C is not slow” compared to assembly.

[b]: Ignoring optimizations. Machine code is as close to the processor you can get.

You would see USENET posts back in the 90s about C/C++ vs ASM basically arguing that C/C++ was better than ASM because hand tuning ASM wasn't worth the man-months of effort required to extract the small amount of additional performance. It's basically the same argument, writing in a higher level language provides more benefit than the performance losses that aren't really that big to begin with.
Probably because C doesn't have a garbage collector or interpreter. And despite all the evidence suggesting that bytecode interpreters on top of JIT compilers with generational GC is plenty fast enough for a huge number of applications, with additional benefits like memory safety and lower development time, devs are still convinced that big == bad and slow.

Maybe. Probably not as bad and slow as you think it is. .NET and Java really don't have the performance overhead that everyone thinks they do, but a corollary of that is C and C++ aren't as fast as you think they are. All that GC tuning you see on JVMs is the same work you do converting to data oriented code in your favorite systems language, you're just fighting the memory wall. While GC's can do it smarter than a human can, they just can't do it for free.

Because people equate startup time with speed? Seriously, Java was never slow compared to the things that I actually saw people comparing it to. I've seen people pick Javascript over Java, because of performance and it just made no sense. Notably that incident was over 10 years ago, when JS was actually pretty slow relative to today.

I can't think of a time when Python, PHP, Perl, or Ruby were faster than Java and yet I saw people acting as if they were.

Your argument is basically myths and misconceptions don't exist, which seems pretty weak.
One reason is that Java GUI applications were noticeable and annoyingly slow to run, compared to native Windows applications.
C# is not faster than Rust - but you can get near Rust performance with all the safety and memory management goodness.

On benchmark C# is giving go run for its money! But again, it is easy to get carried away by benchmarks. Go-routines are a killer feature, .Net async/await come with gotchas.

The weird space of .Net/.net is because the first 15 or so years - it was hostage to Windows. There by tainting its mind-share and engineering culture.

Links (a bit old ones about C# low level chops) https://mattwarren.org/2019/03/01/Is-CSharp-a-low-level-lang... https://www.youtube.com/watch?v=mLX1sYVf-Xg

> .Net async/await come with gotchas.

What are the gotchas?

The only snags I see with async/await are when developers don't know what they're doing and try to force async methods to be synchronous with .Result. If you async it all the way down the stack: your web controller methods are async / your program's main is async, you'll have no issues. It's actually great, because you don't even have to know the internals or what it's doing.

The gotcha is method tainting. You have to duplicate every waiting method if you want to support synchronous and asynchronous API. And it spreads.
There's a performance penalty which happens if you don't need async (eg, if the innermost thing is actually using Task.FromResult), and I've never tested but there's probably some cutoff point where if an awaited operation is faster than x, it's faster to write non-async code (though x may vary by cpu or some other factors).

Practicality speaking, this isn't much of a concern for most code, though. I've only really run into this in a couple specific scenarios: unit tests, and when using something like a message bus or cache layer that has a local/in-memory implementation. I noticed only incidentally (as opposed to because of a perf problem): on unit tests where the time got slightly longer when we changed to async, and in the message bus case when doing some performance profiling and using the "local" dev version.

If there's no actual yield, async/await IS synchronous.
The method still needs to allocate a Task, though. ValueTask along with more aggressive placement of structs in registers helps with that in more recent .NET versions, but it's still not entirely free.
Async / Await can actually happen synchronously if there is no yield.
This is principally a throughput-vs-latency thing, right? Async promises better throughput because concurrency, potentially at the cost of latency.
Async/await can do more than goroutines but because of the explicit nature you do need to be more careful. Not being foolproof is certainly a gotcha.
Did anyone ever think it was slower that Python?!
Gonna be honest, .NET seems to have a weird cult following on here and Reddit but I find the naming to be absolutely atrocious and I hate developing with it. Everything is .Net including .net classic but not everything is compatible making documentation a huge pain to find. It’s also ridiculously over complicated and enterprisy with a lot of heavy weight boilerplate that reminds me of Java. It also copies a lot of the mistakes of the spring framework where you’ll have side effects or code paths that occur that aren’t really able to be found with a debugger. The language itself is fine but the framework around it is just repeating all the same mistakes of everything else

One of the reasons I love go is that everything seems to be explicit. Notice something occurring? Well chances are you can set a breakpoint and trace what’s happening

>It’s also ridiculously over complicated and enterprisy

What does it mean? dependency injection?

There are some older libraries which are horrible enterprise-ware: WCF (Windows Communication Foundation, think SOAP) and older Entity Frameworks in particular.

From the tone of the OP I would assume that things such as encouraging good engineering practises such as good interface design and using appropriately sized units of code are also "problems" with .Net. You hear that a lot from inexperienced developers.

The thing is - the modern framework is far nicer and has largely avoided the problem. Even to the point that I prefer occasionally having to deal with some of the ugly enterprise libraries to the constant hunting down of broken dependencies, vague runtime bugs or massive framework churn you see in the Python/Javascript world. Or having an overly verbose and ugly language like Go or Java.

Awww gee .. enterprisey used that way just seem to mean 'has features i don't need right now'.

WCF had problems with configuration complexity, but that was because it was ambitious! It had so many capabilities and provided a uniform configuration interface for all of them, so it was complex.

.NET was built as a platform to allow ALL sorts of programming, not just beginners level - therefore it comes with a degree of complexity.

I've come to appreciate that mostly people calling it 'too complex' or 'enterprisey' just haven't understood why someone else needs the features they don't understand yet.

I definitely don’t find modern .NET (ie .NET Core) to be overly enterprisy or boilerplated at all. Certainly nothing like .NET framework or Java.
Gotta admit GP has a point about naming though.

Search for info on .NET and you will find stuff on ASP.NET, .NET Framework, .NET Core at the very least. Probably more.

You have to really know what you are looking for and that's hard sometimes because everything is just .NET

This was made even worse when .NET Core was renamed to just '.NET', making it difficult to get good, specific results when googling.
Simply search for ".NET 5" and ".NET 6" including quotes.
I very rarely search "dotnet". Usually searches are based more around the specific technology/library/namespace in the ecosystem I have questions on. This feels to me like a non-issue.
You haven't seen nice MVC middleware pipeline configurations with a decent number of services (DI).

Sometimes I feel like being on JEE 1.4 days.

Gotta love those enterprise architects.

What's so enterprisey about the modern Host/DI? Honestly seems really clean and thin to me.
Nothing, IMO. DI, when used properly, significantly cleans up code bases. It's code bases without DI that often become both untestable and unwieldy.
The keyword here is used properly.

Now go on to work on codebases with rules that every class must have an interface, there are no news, everything must come over service locators, 3rd APIs aren't accessed directly, rather over wrapper classes,....

Ah and no PR ever gets accepted if the rules aren't followed.

Any language that permits any style choices at all is going to be vulnerable to self-inflicted rules requiring bad style.
Just like any language used in enterprise shops is not free from enterprise space architects, and .NET is no exception given its customer base from Microsoft products.
Codebases with rules that every class must have an interface, there are no new in use other than basic stuff like arrays, everything must come over service locators (configurable externally), 3rd APIs aren't accessed directly, rather over wrapper classes,....
Sounds like a problem with your devs/architects/process than the language stack. It would create a mess out of Java or Go or Ruby too.
This .net core good, asp.net MVC etc bad.
I can't agree with this assessment. I'm curious as to what environment you are comparing it to?
A list of the top ten weird cult followings on HN and Reddit would not include .NET
If anything I see way less discussion of C# than it merits, given its key role in the world's most popular game engine.
The thing i see a lot of people bringing up as the large problem with .Net is naming and branding. I could agree with those sentiments but dont really how those are serious criticisms. Thats like saying i hate Java cause i prefer tea.

Anyway if you hate developing in .Net dont do it but I personally find it a lot more enjoyable than other languages ive worked with.

'The problem with java is not java, it is java developers.' .NET certainly enables the same ridiculous layers of enterprisey junk, and even promotes them in older default frameworks.

But like any language, it just takes discipline to stop your team building this bloat.

A CLI app in .NET is as clean as you build it, and sensible frameworks are now available for web stuff too.

(comment deleted)
I think pretty much every .NET developer would agree with you that the naming and versioning has been an absolute clusterfuck.

It is starting to settle down into a sensible place since .NET 5 though.

If only they would drop ".NET" and "C#" and officially replace them with dotnet and csharp in all material.

Some unique searchable term would be great. As other replies point out, .net, c#, dotnet, and csharp are all collisions with old documentation.
You can use date search in Google, you know?
> Everything is .Net including .net classic but not everything is compatible making documentation a huge pain to find.

I wish they would have just called the new version ".core" and be done with it.

For a long time, it was difficult to figure out if a particular piece of information you found by googling ".net" was for the old or the new. Things are improving though, as the old version gradually fades, so you can reasonably assume the new version unless stated otherwise...

The author makes a few good points (mostly about performance), but the 'myths' about open source friendly, enterprise development, and cost are disingenuous at best, and just plain wrong at worst.

I work in a large corporate environment, and .NET is expensive. Nobody just 'uses the Community Edition', because there are pre-established licensing arrangements and costs. I've seen the costs.

.NET, whether it's good or not, is slowly going the way of the dodo. What keeps it around is the corporate lock-in they've established for the Microsoft stack, and that's a hard thing to crack.

Senior SE working in a company which has its services built almost exclusively in .NET. C#/.NET is not a major contributor to our expenses - the language of choice could've been Go, Rust, Python or, god forbid, Java - just replace Rider or VS Code with IntelliJ or other tooling and use a different base image when deploying to k8s. If you use MSFT products you might be incentivised to use C#. However, there is no incentive to not go C# when you use something else. I also found OSS process for the core libraries and runtime to be very inclusive - everyone can contribute as long as the change produces measurable and meaningful improvement serving interests of a wider audience.
SQL server is expensive, and windows, office etc can add up. Postgres works fine in from .NET.

As for Visual Studio, Professional is all you would need and it's not too expensive, I saw $45 a month mentioned, which is around $2 a working day.

In my opinion, it's actually kind of a good thing that it's not the cheapest option, because it tends to avoid the bottom feeding employers who are more likely to scam their employees.

In terms of language C# always seemed to me like it was a language that expects you to use a (the) IDE, much like Delphi. Which is really unfortunate in my opinion, because I usually read code outside an IDE and also spend more time strictly reading code than writing.
Don't you want an IDE to navigate through the code (go to definition/implementation, find all references, etc)?
Not sure about you, but I prefer environments that can be navigated without IDEs so that when I add IDEs and tools I become an overpowered developer; instead of having to add the best tools available just to become an average developer.
All environments can be navigated without IDEs. You can write code in notepad, it's just text after all.

The tools don't make the developer, and the truly expert developers focus on productivity and results, regardless of environments.

Yup, all environments can be navigated with notepad, but some environments are easier to navigate than others. For example, Yaml vs unindented Json. They represent the same, yet in one I can see what's going on with the indentation, and in the other I have to go hunting for closing braces.
doesn't help that the delphi IDE isn't very good
Back in the day, Delphi had the best form designer by far, which to this day is still unsurpassed in some aspects (visual component inheritance anyone?).

The code-oriented portion of the IDE was also decent, IIRC, with very fast edit-compile-debug loop (something Borland was traditionally good at from TurboPascal days), a capable debugger, and excellent hyperlinked and contextual documentation.

But I haven't used it in more than two decades and it makes me sad if it's really true that it's no longer good.

Winforms supports Visual component inheritance, though not as nicely as Delphi used to around V3-5 which I used.

I also loved Delphi's WebBroker technology, their visual web components were awesome and that tech was so far ahead of it's time I still pine for it. Was writing compiled ISAPI web performance, with fast development, huge amount of high quality libraries, and hot reload! In the year 2000! The only tradeoff was manual memory management, and I guess lack of generics and LINQ.

How is this different to any other language that uses more than one source file to define a program?

    using Foo;
    using Bar;
    using Baz;
    // somewhere else
    var foo = new Bublicon(...);  // where tf does this come from?
vs.

    use std::option::Option::{Some, None};
    // ...
    Some(1234)  // clearly std::option::Option::Some which I can then go find easily
> // where tf does this come from?

Just hover over it, and you'll find out. Or Ctrl+click to navigate to its implementation, possibly decompiling from IL if the source is not available.

> use std::option::Option::

C# can accomplish similar effect through using alias directive.

The actual title is: 6 .NET Myths Dispelled — Celebrating 21 Years of .NET

And they forgot 2...

Myth 7: The .NET branding dept sucks.

Myth 8: The .NET desktop strategy has sucked since Build 2011.

Oh wait, both of those are still true.

> The .NET desktop strategy has sucked since Build 2011

only if you need cross platform. WPF is pretty good and WinForms still ain't broken. oh you said strategy .. I think the strategy is wait for something to get popular, maybe something will happen with BlazorWebView

Winforms is my 9-to-5.

But hHow many new official-way-to-do apps have there been? I've lost count.

Actually, what's the current one(s)?

Do not start new projects on WPF. WPF is considered done, meaning no bug fixes whatsoever. There is nobody working on it, not a single person.
WPF is still used heavily in Visual Studio.
This is a ridiculous piece of FUD.
You say that it's "FUD" but why is point 1 of the roadmap still "Incorporating .NET Framework servicing fixes into .NET Core 3.1, .NET 5 and .NET 6"? It has said this for years now.

Nearly all commits are bot commits updating dependencies, some are to keep the CI process up to date, and all others only fix typos or other strings.

2 months ago somebody fixed a few things but no activity since then. Microsoft is trying to transition WPF into a community project as indicated by the current roadmap and I don't see this meaning anything different that them abandoning WPF for good.

Not sure why you're being downvoted, as these are real pain points. As other comments have pointed out, .NET rebranding sucks and is confusing (like .NET Core, .NET Standard, etc.). And the questions around the GUI toolkits don't help. WinForms is the only thing that is (still) consistently developed - kind of.
I do not think it is an issue about (re)branding. Each of those things that people refer to as confusing (.NET Framework, .NET Core, .NET Standard and now standalone .NET) are distinct and separate parts. I think it's more that .NET spent the best part of its existence Windows-only and to transition to being cross-platform requires some hefty changes that necessitated clearly defined stages (windows-only .NET Framework, then early-stage-cross-platform .NET Core 1/2/3 and then end-result .NET 5/6+) and a clear way to write library code targetted such that it can be used by certain cross-sections of those stages (.NET Standard).

They've managed the transition well on the technical side of things - as you can see there are plenty of people who are happy with the end result. But it's clear from comments that pop up here that they've really missed the mark when it comes to actually describing the intent behind the whole project, because a surprising number regard it as just an unnecessary exercise in rebranding.

The WinForms repo is actually also quite dead. There are fixes here and there are but no major things in the horizon. Apparently they want to focus their efforts on MAUI and WinUI, both of which are not even close to production ready.
That has been a consistent pattern in .net, where the liked current tech withers on the vine to make place for the new thing. Instead of the new thing proving itself organically.
I don't agree that these are real pain points.

Try to build desktop app in Ruby or in Python, no one cares about desktop apps. If you want to build native desktop apps you go with QT or something C++ based.

.NET branding being confusing is a theoretical problem. When you work day to day with the code you don't care and it does not affect anything I work on. It is not like it somehow affects compilation or some of my code stops working. I use .NET 4.8, .NET standard and .NET Core and now .NET 6 in projects and no problems there.

>no one cares about desktop apps

I take it you never used Photoshop, 3D Max, Autocad, Word, Excel, Visual Studio, Paint or Notepad.

Word and Excel already moved to browser editions not to mention Google productivity tools.

Check out web.autocad.com

There are more and more painting tools available online.

Cloud based IDEs are popping up more frequently.

Not to forget about all web based note apps.

That is the trend and no one cares about creating next Photoshop or Autocad competition on desktop.

Let alone that I mentioned that if someone cares about building desktop app he goes with C++ and QT or something like that and not .NET or Ruby or Python.

> .NET rebranding sucks and is confusing (like .NET Core, .NET Standard

We have no more CORE, just .NET.

>And the questions around the GUI toolkits don't help. WinForms is the only thing that is (still) consistently developed - kind of.

MAUI is going to be released soon.

Who cares about desktop strategy?

Have you tried to make a desktop app in Ruby or Python? Java desktop apps are also not having some great approach where stuff was changing through years AWT, Swing, JavaFX.

Of course Electron is winning but it is basically a web app wrapped around.

That is such a moot argument.

> Of course Electron is winning [...]

Winnig from the side of the business or developer, but absolutley not as a user. UX ist just so bad with many Electron apps. They feel slow (and usually they are), they miss a lot of OS specific usability. That's not a win-win.

It is winning if we discuss in context of ".NET desktop strategy sucks" and Ruby/Python not having any strategy for cross platform desktop UI.
Myth 9: .NET development is dominated by Microsoft, not a community

Myth 10: if Microsoft decides to kill .NET or go in a new direction, you are screwed (at least more than with the mentioned community-developed open source alternatives).

I'd be happy to see these myths dispelled.

Is there something confusing about just ".NET" as a brand moving forward? It was annoying at first when we went from .NET Framework -> .NET Core -> .NET in a short period of time, but it's just been .NET since then, which seems fine to me.

Nobody in this thread has mentioned WinUI yet (https://microsoft.github.io/microsoft-ui-xaml/) which makes me think very few people actually know what they're talking about here.

Author should submit it to ms propaganda/evangelist department to get a MVP medal for best bs of the month. Of course MS bs regarding .NOT is nothing new. It was windows only for 21 years and marketed as portable/high speed/modern/backward compartible all that time. All that time it was a cherry picked lie. For the last 6 years they not even mange to offer GUI library. Try to use Winforms. You can’t? Too bad, linux is just for servers anyway, right? Go get Xamarin trash and suffer. “We are on journey, busy with our foundations/pr so fk off.” The right answer was not changed for 10 years: - never use ms tech for web, or you be vendor locked and ripped off - ms technologies in core not changed since 00s, it is same old st in a layers of chocolate. As soon as you get inside, you puke. - never trust ms or you be vendor locked and end up with inferior legacy technologies, without reach to support and with huge tech debt and eventually need to just throw away everything you build - remember, facebook and google not using ms, your grandma do
If the language and framework has been changing isnt it a good idea to inform about these things that used to be true but no longer is.

"never use ms tech for web, or you be vendor locked and ripped off"

How exactly do you mean this, .Net can be run on most platforms. Or could it be that you didnt read the article since you assumed it was propaganda?

The charitable interpretation of the parent post is that the .NET team has lost all credibility, and that one should not trust them to make developer-friendly decisions.

I don't know whether or not that statement is true, but I think this is the point worth discussing.

>but I think this is the point worth discussing.

then somebody has to say some some specific stuff instead of "ms will lock you"

It will, go net, and your grow ms consulting/azure/sql server and at the point of having windows net containers you already paying ms thousands for visual studio premium / azure dev/test subscription/reservations/devoos services and office 365 just because ms told you so.
I do use .NET on Linux on VPSes in OVH or baremetal with sqlite/mongo/postgres/sql server.

I do use Excel/Word/Outlook etc because companies provide it for me

I do use Visual Studio because I believe it is very good IDE for C# + I know it very well

None of the things you mention are required or in some cases even connected to .net or c#.

Azure - is not required to run .net, and .net is not required to use azure.

Sql server - has no advantage on .net than any other wellknown database.

windows net containers - Not sure what you are talking about here but if you use containers, docker linux containers are the default choice.

visual studio premium - As the article mentions there are many alternatives here. For example Jetbrains Rider or VS Code.

dev/test subscription/reservations/devoos - Maybe you are talking about Azure devops. But this is in no way required by .net.

office 365 - Has no whatsoever connection to .net other than being developed by MS.

Is this some kind of ignorance joke? I must be missing the punch line.
Of course it is! Net is another ms’ ignorance joke. And punchline is, you always pay ms.
just checking your comment history and why you are on negative points, you seem to be having a bit of a culture mismatch with HN in your approach to commenting, check https://news.ycombinator.com/newsguidelines.html, I guess the relevant section is :- "Be kind. Don't be snarky. Have curious conversation; don't cross-examine. Please don't fulminate. Please don't sneer, including at the rest of the community."
> .NET isn’t open source friendly

The language runtime might technically be open source, but it’s got a huge number of closed-source packages and the ecosystem appears to be only begrudgingly open source.

> It’s slower than Node/Python/Go/Rust

One of 2 of these things, aren’t like the others. I’d believe modern .net can outpace Python, but matching or outpacing Rust is something I’d have a hard time believing, especially in the “vast majority of code” sense-I’m aware that .net can be written to stack allocate everything. I’m also aware that basically no .net code I’ve ever seen in business world has ever done that (precluding games here, because I’ve not worked with them).

> It’s for boomer enterprise development

Yes. Uncharitable presentation and framing, but yes. Every single place I’ve worked at that has used .Net has had the same attitude and approach to solving problems and writing code. Use Kafka for our events like every other company in industry? Nah, use some bizarre MS thing that nobody outside of .Net has heard of or can use. Use websockees or SSE? Nah, use some bizarre .Net version instead. Use Postgres because it’s more than good enough and other teams might need to use the database? Nah MSSQL, and all the tables are named and optimised according to EntityFramework and if you have to suffer through ODBC issues that’s a you problem!

Never mind the obtuse naming and the dogged insistence on dependency injection at every possible turn.

Am I not a fan of .Net? Correct. Is it because every time I’ve had to touch it, it’s a painful and frustrating experience.

You know that Rx in .Net kind of inspired all of the popular reactive libraries/frameworks, right?

Other than that: if you go to a .Net shop who want Microsoft Certified Developers and you expect anything than low quality-stay-on-the-Enterprise-support-contract-rails then that's the problem.

> Nah MSSQL, and all the tables are named and optimised according to EntityFramework and if you have to suffer through ODBC issues that’s a you problem!

That's not in any way true - it's an ORM and it can handle any database naming scheme you happen to have. I still wouldn't use it - ORMs in general are a bad idea outside of a couple of use-cases (i.e. CRUD-heavy systems).

Look up the Microsoft Community/Open Specification Promise and their patent promise https://github.com/dotnet/runtime/blob/main/PATENTS.TXT

.NET Core is open source, free, and comes with the guarantee of no litigation from Microsoft. As far as the ecosystem, it fully embraces open source too. At my company, all .net libraries we use are free and open (AWS, Consul, Elasticsearch, EventStore, Kubernetes, etc).

A lot of valid points. I think CSharp is a very nice language, I think dotnet has potential but there's a lot of rotten tertiary layers in the onion
> Never mind ... the dogged insistence on dependency injection at every possible turn.

You will do yourself and your career a favor if you learn it!

I did learn it when I wrote .Net.

I understand it fine, but I think it's far too situational, and greatly overrated. There's so much focus on "abstraction magic" and a desire to avoid admitting you have a dependency or daring to ever actually implement something. It made writing code and debugging code more annoying (everything shows up with "0 references" in VS because...it's all behind DI magic). To say nothing of the verbose, boilerplate setups for it, which were closer to actual incantations than logical code.

I'm sure there's more lightweight ways of doing it, or we should have used a different DI tool, or it shouldn't be done x/y/z-way anymore, but the reality of most DI-afflicated .Net code that I've had to deal with is that it's never done 'well' and all it did was add noise and code that was more difficult to write, debug and maintain.

It falls in the same bucket as most of those other OOP/SOLID/design-pattern principles - half-decent situational guidelines for software that morphed into what amounts to 'holy covenants' that must be strictly applied at all turns.

> It made writing code and debugging code more annoying (everything shows up with "0 references" in VS because...it's all behind DI magic).

VS shows all references for code, even if the code is called through DI. If you highlight a method call or a property and press CTRL+F12, you will actually jump to implementation.

>It falls in the same bucket as most of those other OOP/SOLID/design-pattern principles - half-decent situational guidelines for software that morphed into what amounts to 'holy covenants' that must be strictly applied at all turns.

I actually dislike GoF patterns, SOLID, Uncle Bob principles and OOP in general, but DI in .NET is quite decent now. Maybe your experience is from old .NET Framework.

What language have you moved on?

> VS shows all references for code, even if the code is called through DI

Maybe your setup is better than mine, but I distinctly remember ctrl+f12 not working for a bunch of stuff and having to keep stacks of tabs open.

> Maybe your experience is from old .NET Framework.

Was a mix of framework 4.5 and core 3.1. The other devs worked across both, the projects I was on were core 3.1 with C# v8 specified, basically as close to “as modern as I could get” without running into compatibility issues haha.

> What language have you moved on?

Rust for personal projects and some work stuff-it’s by far my preferred language-, the rest of the work stuff is Scala and Java, largely dealing with Spark.

We used .NET for writing microservices running in Kubernetes. We used Elasticsearch and Kibana to store and search logs. As data stores we used Postgres and Redis. Consul was used for configuration. For mesagging we used NATS as Kafka was too fat and slow.
Unfortunately Nuget package management is abysmal, and a development environment lives and dies by that nowadays. There's also the legacy reliance on XML that shows itself everywhere in C# development. It's easy to forget that the reason Node took off exponentially when it did is not really because it was that great itself, but because NPM (and its' small package philosophy, in combination with JSON based config) was a bit of a revolution in package management that led to network effects of adoption.
>Unfortunately Nuget package management is abysmal, and a development environment lives and dies by that nowadays.

are you saying that there should be more packages in nuget?

>There's also the legacy reliance on XML.

you mean project file?

or WPF's XAML?

>you mean project file? or WPF's XAML?

Those, and many other things. XML is just an embedded idiomatic part of the language. There's nothing really wrong with XML, but the web runs on JSON now. And all other modern languages (Go, Rust, NodeJS, etc.) are built with that in mind first.

Well,

.NET has Newtonsoft Json which makes working with Jsons pretty easy, the built-in System.Text.Json probably works well too (I didnt use it directly yet)

When it comes to XAML, then XML-like structure is way better for describing interfaces than Json

After all, the web is built on HTML which's kind of similar.

System.Text.Json is a handy improvement over the Newtonsoft stuff. Faster and has great support for annotating your classes to do things like remove properties that aren't required when you send them externally. A bit painful to move across but well worth it.
> XML is just an embedded idiomatic part of the language.

That's kind of weird to say these days; I never ever deal with XML in .NET. I'd go as far to say it's 1000:1 JSON to XML for me.

What are the many other things that use XML? And why would these be better with JSON?
> "Nuget package management is abysmal"

Why? Explain more.

> "legacy reliance on XML"

What reliance? Are you talking about .csproj files? They're very minimal now. XML vs JSON is an old discussion: both are trivial to deal with when small but get unwieldy as they grow (however XML has much better structural support).

Also the .NET configuration system reads from JSON files by default.

> Why? Explain more.

- Why do some packages behave differently depending on whether they were installed from an install or restore command? This shouldn't have any observable difference, sync (restore) should be the only operation that touches the package cache at all.

- Opaque reliance on invisible plugin hooks. Back when I had to use it, it was pretty common to get told "oh, just add this random dependency" to fix whatever weird behaviour NuGet got up to that time. Why or how did it fix it? Nobody really seemed to know.

- Binary package managers suck. They make your supply chain more vulnerable, and they are an additional repository to set up and manage. And they tend to lead to losing the knowledge of how to do a full source rebuild at all.

It sounds like you're talking about outdated experiences (on topic with the article). Install/Restore now are the same operation, and all nuget ops are part of the main dotnet CLI which is used by the IDEs and other tooling. No plugins needed because everything is consolidated to the same process.

As far as the supply chain, .NET assemblies are IL (intermediate language) and can be inspected and signed to ensure integrity. There are trade-offs but I find it much better than the source-first mess of NPM. Go is interesting but only recently got official module approach and still relies on convoluted path-based references.

I don't see how you'd lose knowledge though, you're still building your own source. You can always copy the raw code from an (open-source/decompiled) package into your project and build that way if you really want to.

Its almost as if people forget that git submodule is a thing, if they want the source from the packages
Git submodules are indeed a thing, and they're even supported by the `git` CLI, but I'm not sure I would go so far as to say that they're a first-class feature of Git :P
> Install/Restore now are the same operation

Finally, I guess.

> and all nuget ops are part of the main dotnet CLI which is used by the IDEs and other tooling.

Okay? I don't really see what the difference is between `nuget foo` and `dotnet nuget foo`.

> No plugins needed because everything is consolidated to the same process.

Err.. what?

> As far as the supply chain, .NET assemblies are IL (intermediate language) and can be inspected and signed to ensure integrity.

Sure, and you can disassemble all the x86 binaries on your system, too. But that's not the source code that people are reading or writing, nor is it what's tracked in your Git repository. Even if it's signed, the most you have to go on is "the author promises that this is the same as the corresponding source release".

> There are trade-offs but I find it much better than the source-first mess of NPM.

This is a fun example, because it NPM is also binary-first and has exactly the same problems! (Except arguably even worse, because your typical package will ship in 3+ different forms, and good luck finding out which one is actually used when).

For an actual source-first package manager, take a look at Cargo, Nix, or Cabal.

> I don't see how you'd lose knowledge though, you're still building your own source.

You're building your own code (for now), but not that of your dependencies.

> You can always copy the raw code from an (open-source/decompiled) package into your project and build that way if you really want to.

Sure, but you shouldn't have to give up on package management to have a reasonable and trustable build process.

I like where you're coming from, but .NET also supports an ecosystem of commercial developers who sell commercial closed source libraries, as well as open source.

I also had some issues with Nuget, but fair to say that's been the case with pretty much every package manager I've used.

Re trustable build process, some of the .NET team has been blogging about reproducible builds in the last year.

I'd say NPM took off because it was a package manager for a language that barely has a standard library. Framed that way, every new node dev learns how to publish 6 lines of junk to npm.. i guess you could call that a network effect!

Nuget is not terrible, its not as simple as npm but not far off. Anything that uses native code is painful, but then so is node-gyp..

NuGet used to be trash, but now with the new project format it's just a single reference.
I used .NET Core for a backend API relatively recently (3.1) with no background in .NET prior (mostly javascript/typescript, php, and ruby). I found it incredibly productive and to be an enjoyable developer experience. Entity framework is one of the best ORMs I've ever worked with. Solutions and projects took some getting used to but I like it in retrospect. I now use IntelliJ products as a consequence of having used Rider for that project (developing on a mac) and enjoying the experience. C# as a language is now one of my favourites. I find it hard to convince colleagues and peers to give it a chance (mostly ruby and javascript devs) which is a shame.
> "incredibly productive"

That's the best summary of the .NET stack, unfortunately it requires first-hand experience to figure it out.

Same goes for Elixir and Scala, it's mostly because from an outside-in view it really hasn't changed much (when 'looking' at the language and framework in question). Technical changes are relatively easy, cultural changes.. not so much.
It depends what your trying to do and anything worth using has some learning curve. Hardly something I would say is a downside of .net
In rails I can drop into console and play with APIs in repl. What’s the .net equivalent?
.NET languages have REPLs too.
Oh, so you can do things like User.where(name: “Bill”).pluck &:id

In the repl?

I have no experience with .net and it looks like these repls are new

I think at least some of the REPLs came about with the new compiler architecture, yes. That isn't exactly new anymore, but new compared to the overall age of .NET.
Some time ago I had the misfortune to direct a C#/ASP.net web project, and that project turned out to be the first web project where the backend was singnificantly later than the frontend. Even though the backend had 2 senior developers and the frontend only 1 junior developer doing React.

Since then I am not able to take seriously the saying of .net to be "incredibly productive".

This comment could benefit from development. For example: how do you know .NET was the cause? Anecadata can be useful in aggregate, but not with such sparse details.
(comment deleted)
What do you mean by direct? Were you doing the coding? And the backend was larger in what way? And is that a problem? And was the frontend a SPA? Was asp.net just used to make a JSON API?

What were the actual issues and what would've made it more productive? That's much more useful for discussion here.

I initially directed (as organise stories, unblock people, do some devops & code review), but eventually I had to jump into the backend, because, as the most senior developer, I'm expected to jump into whatever part is having trouble.

The backend had to receive events from a webhook and accumulate statistics on those events, then serve these statistics via REST API

Frontend was plain old React reaching the REST API.

Issues:

* The database (MS SQL Server, as recommended by Microsoft) would randomly lock up for days. Bear in mind this was a 1GB of RAM (the minimum amount that Microsoft accepts as enough to run a SQL Server instance) RDS serving around 600 MB of data.

* The introduction of the Repository Pattern (as recommended by the ASP.net documentation) introduced a lot of complexity (including incompleted/non-defined operations) that slowed down significantly the development.

* LINQ supports interleaving DB operations with not-db operations, quickly becoming a O(n^m) footguns. If you make `row => row.x > 5` a method, LINQ will happily download all the rows and run the comparison in program instead of in the DB. Bear in mind I can't construct these footguns with django ORM, or make them typecheck with any Haskell data access framework.

* The test runner UI would pretend no tests existed if the code didn't compile for any reason. Trying to debug something by running a specific failing test? If you miss a semicolon now you have to go find your test again.

* The logger would log the message and the affected class in different lines. Do you want to grep for an error? You better know the obscure grep syntax for "previous line". Want to change the logging format? Good luck finding the code that adds the newline in order to find how to influence it. If you ask the IDE to find the implementation of the logging function it will show you the interface, nothing more.

* You better not do some type of async stuff in LINQ, because the system is going to spawn a process for each entry, obliterating your production instance by creating thousands of processes, using a bunch of MB for each process.

There were more issues, I didn't make an exhaustive list of them because, you know, I don't need to make exhaustive lists of malfunctions and bad design ideas for other programming languages.

Based on this anecdote, it sounds like the team had little or no .net experience and your seniors probably don't deserve the title.
Curiously, I haven't had these problems in Python/cherrypy/django/flask, Haskell/snap/yesod, NodeJS/express/nextjs, so, maybe it's something beyond lack of .net experience.
Your experience mirrors mine, both early (circa 2005) and more recently.

Footguns are common.

I am not sure where the .NET community coalesces (python and similar tend to hit up IRC).

The ability to find and get answers on stack overflow is pretty impressive, although that might not be the “community” your looking for. I’ve had decent direct engagements with them framework dev tends on GitHub before as well.
To add on to community -- it's more than that as well, it's the zeitgeist. In the scientific computing world, if something has hit NumFocus' portfolio then it's active, maintained, accessible, and (IMO) can consider adding into a tech stack. Similar for Apache for general FLOSS projects.

I don't understand the dotnet's extension ecosystem -- how to find quality sourcing, etc. Going from an established project into dotnet SDK makes sense. But searching GitHub for useful dotnet frameworks -- stars are probably misleading here?

There's been lots of bad decisions along the way for .net, but In its current state (.net Core 6) I can't really see any of your problems happening. Although what strikes me most is your first point. Running SQL server on 1gb and complaining that it didn't work well isn't really wise. Maybe this happened on the early years of .net Core or even on FF?
I'm following what says at the official Microsoft documentation.

https://docs.microsoft.com/en-us/sql/sql-server/install/hard...

Am I unwise for following the official Microsoft documentation for a Microsoft product?

Yes, I'm unwise for following it. I shouldn't have accepted the project because it required Microsoft technology. That would be wise.

The documentation you linked to clearly states that they recommend 4GB of memory and more for larger databases. While it CAN run on 1GB of memory, it is also therefore NOT recommended. You chose to go the route of the unrecommended.

Also, just sounds like you hate Microsoft, and are looking for (poor) excuses to continue your hatred.

We were using the Express edition, and I don't think a database needs 4GB of RAM for serving 600MB of data in a glorified CRUD and filter application that receives 1 update every 2 seconds, and serves a bunch of queries every 5 minutes. An unoptimized Sqlite can do that easily without the crazy memory.
That makes more sense, sorry for the confusion. I don't think your expectation is unrealistic. I haven't run into anything like what you experienced, but my exposure to express under tight memory constraints isn't something I dabble in.
Did you perhaps use dynamically generated queries, instead of stored procedures? Or do a join onto either table x or table y?
The dynamic queries are compiled on first run, with a query plan optimized to the size of data - that's really unlikely to be the performance issue.

Was the server running Windows? Windows doesn't perform really well in 1G of RAM, especially if you're running GUI Windows and not Windows Server Core. Then running SQL server on top of that, well that 1G RAM probably IS the performance issue. A good rule of thumb is Window takes 3G, and your programs go on top of that.

There were clearly other issues from what the OP described though, and to use LINQ you do have to have a good clear mental model of the runtime actions that it give you. It's a power tool, and can let someone experienced develop very fast, or let someone inexperienced cut their fingers off equally fast!

To provide an answer to OP, about following MS recommendations .. the reality is that not all MS advice is good advice, and also that with so many versions not all of their documentation is up to date. However it seems your team did draw the short straw! Mostly their documentation is very good for product and API level documentation, not as good for architectural documentation.

In my experience the biggest problem with C#/.NET tooling is the lack of too many open source 3rd party alternatives.

In the .Net world, if MS released a certain library, that would become the de facto choice for nearly everyone in the ecosystem. Competing with the MS options was almost impossible.

So even if the MS choice had a lot of flaws, it would remain predominant.

Which meant that as long as the MS option was good enough, you wouldn’t see too many alternatives unless they were an order of magnitude better.

And a large reason for this was the closed source nature of .NET.

In the open source world, even if the primary vendor option is great, but it has only a single minor issue, someone will invariably fork it and fix the minor issue. Which puts competitive pressure on the primary vendor to incorporate that fox, or if they’re unable to, see a lot of people migrate to the competing, slightly better offering, which is easy to do since it’s basically the same product with that 1 slightly better fix.

In practice the original vendor almost certainly incorporates the Fox, especially since it’s so easy to do as someone else has already written the code for it.

So IMO the problem with the .NET/C# ecosystem used to be it’s closed source nature, which led to very little competitive pressure for improving tools and libraries, unlike all the primarily open source alternatives you mention.

> In the .Net world, if MS released a certain library, that would become the de facto choice for nearly everyone in the ecosystem. Competing with the MS options was almost impossible.

While I get your point, I personally prefer it this way. Whatever Microsoft will put out will at least be “ok”, regularly bug fixed and supported for ever and ever.

I prefer to having an ok default and just rolling with it.

And having a default library for everything is great since you don't have to learn countless libraries doing the same thing and you can understand code written by others with ease.

But this is a tacit agreement in the .NET world. It's not only about libraries written by Microsoft. When sensible libraries such as Automapper or Newtonsoft JSON appear, everybody defaults to them.

And there are alternatives. But the core libraries are a safer bet for long term maintainability, and they get invested into to survive long term.

A big thing that people often don't understand about .NET is it has the perspective of being all things to all people.

One example of this is that it gives first class support for supporting living extensible systems for long durations like decades which is often not really the case for other stacks. Hence the prevalence for approved long term supported libraries typically with first party backing from MS.

Another aspect of this all things to all people is that it has lower level closer to the metal performance features that people only used to high level stacks often mention they don't see the need for (although they don't say it like that!).

Another aspect is you can do pretty much any kind of application in it, from web to native to high performance games or latency sensitive systems (with some special techniques) to well almost anything. The ability to access low level performance while still having high level general ergonomic APIs is critical to this.

For the benefit of others who might get the wrong idea, async/await definitely doesn’t spawn processes and in almost all cases not even extra threads.
Pretty sure he was employing “Task.Run” which is… nearly always the wrong thing but does appear in a lot of the early examples.
Was this the first time using .NET for most of your devs?

> LINQ supports interleaving DB operations with not-db operations

This hasn't been the case since EF Core 3.0, it will throw an exception if something can't be translated to prevent this problem. Prior to this (EF Core 2.2), a warning would be spat out if an in-memory operation occurs. Due to this, I assume this project was a number of years ago.

> The logger would log the message and the affected class in different lines.

The default logging format is unfortunate, although it can be made a single line in .NET 5 onwards. This issue would've been easily solved by using Serilog, swapping out the built-in logging with a few lines of code. I don't know the last time I encountered a .NET dev that wasn't at least aware of Serilog as an alternative.

Good to know for me who used EF5.
Keep in mind I've never done ASP.NET (only desktop development):

The old C# stuff is indeed crusty, intermixed with hot nonsense from the Balmer days.

Latest Framework version is I'd say OK, nothing to write home about, newer stuff is much better (.net 5, 6).

I do think MS SQL Server is a bit, well, bad. But don't worry, irrelevant to the C# stuff - I used to use java servlets to query mssql, while it was better performing than mysql, I wouldn't pick it again.

The anecdote about the React frontend bit is interesting, but ultimately depends on what the UI was doing. JS is certainly good to bang out some quick UI, but if it was shell for the backend, I'd say your app was either mis-designed or just naturally backend heavy.

Again, haven't used (not in web dev right now), but Blazor exists for doing C# web UI, instead of complaining that the backend was bad/hard a better comparison would be between Blazor and React.

Or you could switch architectures and do a non-component-based web UI - faster to put together a mashup of JS junk but it doesn't necessarily perform as well as doing a web client old-style - simple request response works for a lot of use-cases, its not as flashy, but it is fast.

The (Generic) Repository Patterns are some of the most anti-patterns created and goes fully agains't DRY and SOLID, it's like whole generations of developers are just on the same cargo and have never worked with a database( or datasource) having more than 1 table.

So it looks great for a blog post, for a linkedin thought leader article, but when actually used, you end up duplicating the same shit over and over and over unless you use extension methods. But then why having the damn Repo patterns with the basic methods in the first place?

These patterns are just awful and don't solve anything, abstraction of the DB? You don't need that pattern for that.

Agreed. Fortunately the main documentation now shows that this is an anti pattern since EF dbcontext is already a repository pattern and implements unit-of-work semantics.
Well, please explain to me how can I switch the app from using Postgres to Redis or Cassandra with ease if I am not using something like a repository pattern?

It's true, EF abstracts the database away by not requiring you to write SQL but EF is only usable with some SQL databases.

So, having another abstraction layer might not be a bad idea.

Yup, because we devs switch databases every week. That's the real reason why all projects are late: we are so busy switching databases all the time we don't have time for writing code!
dotnet add package EfCore.Cassandra.

Then in startup.cs or program.cs, add this to your services: optionsBuilder.UseCassandra("Contact Points=127.0.0.1;", opt => { opt.MigrationsHistoryTable(HistoryRepository.DefaultTableName, "<schema name>"); });

See more here: https://efcorecassandra.readthedocs.io/en/latest/intro/getti...

Well, that's a new one. I didn't know there's an EF provider for Cassandra. Thanks for the update. :)
> "switch the app from using Postgres to Redis or Cassandra with ease"

You can't. There is no such thing as switching databases "with ease" for any serious application.

Postgres, Redis and Cassandra are all very different databases and it doesn't make sense to switch between them; unless your app is small and still experimental so the changes are trivial, or so large that it's a major project with very specific needs that requires far more work than a repository would solve.

But if you really want to, Entity Framework already has multiple providers including non-relational databases like Cassandra. Anyone can write a provider and there are many 3rd-party libraries for whatever data source you want.

> "having another abstraction layer might not be a bad idea."

It's a terrible idea. It's such an exceedingly rare situation that you're wasting a lot of upfront work for something that'll never be used.

Also .NET has a fantastic data system with LINQ which is built around `IQueryable` and provides powerful query modeling that can be incrementally and dynamically changed before its pushed down to the datasource. It's what makes EF so good and why repository patterns cause so many problems by breaking this interface for no productive reason. You can also use extension methods on the `DbContext` to extract complex queries or large operations to a single method.

Now all that being said, there are times where you can have an abstraction/interface to your data sources if they're very complex or spread across multiple databases or have other processing needs. But at that point you actually need it, instead of just doing it as a "pattern".

Your last point is not how threading or async works in .NET. It’s a single process that has a thread pool that runs async Tasks. The rest of the comment indicates that the team didn’t have much or any experience with .NET.

I’m sure there are issues with learning curve but the ecosystem is very capable (the corollary to being very productive) but you need to know how to properly use it.

It also indicates the ego of the "best" senior dev who leaps in to save the day is quite large, and not very well correlated with ability and experience with the stack.
We also tried hiring. After checking 100+ resumes, and interviewing the best 10 candidates, we hired the best we could find, who turned out to be average.

My ego is not unfounded, I have had to do this successfully in other projects, but I can only do it if both the tools, the codebase and the framework allow me to do it

I would say it sounds unfounded to me. You "jumped" in to a different language, different database, different framework, and you landed up making junior level mistakes.

Your explanations of what went wrong show clearly that you still don't have a grasp of how async/await works. So you don't understand the concept, which is now common across a number of languages including javascript (which you claim to know) and python (which you claim to know), dart, and scala.

Based on your technical knowledge of languages you claim to know, I would say you are closer to a junior developer.

You seriously want me to tell you how async, promises, callbacks, coroutines, closures+continuations, call/cc, and copies of the stack are actually points of view in the same concept?

If I'm a junior, 99% of the professional programmers out there are simply not programmers at all.

His ad hominem was uncalled for. To be completely fair though, your list of grievances (some of which are fully valid) intersect strongly with things most people encounter in their first 6 months of working with the framework.

I feel you on the hiring bit, there's such a glut of resume padders, bullhockey purveyors, enterprise leftovers in .Net it's hard to find good people (especially in the US). If you put feelers out to any recruiting firm you'll receive a who's who of people no one will hire. There's also not much of a community to speak of compared to other platforms (what community there is is spread out across numerous platforms or centered around MS, github) so there's not really anywhere you can post your jobs and expect a positive result.

The hiring thing is maybe a bit of a chicken and egg situation, as there's clear prejudice against .NET in many hackernews posters and low amount of job ads on who's hiring. I've read threads here of a number of people who switched stacks because of this issue, even though as it becoming apparent .NET is a perfectly cromulent stack!

To anyone trying to hire heavy lifting .NET expertise, I am now looking for an interesting fully remote role!

I've been using .NET since V1 and Delphi before that so I have deep exposure to getting things done with this stack and would prefer to work with it compared to most others. I've also worked commercially with about 10 languages so fairly polyglot, just find C# very powerful with a focus on GTD.

You don't have any contact information in your profile :)
I would like you to explain how "You better not do some type of async stuff in LINQ, because the system is going to spawn a process for each entry, obliterating your production instance by creating thousands of processes, using a bunch of MB for each process." translates into how using async spawns processes. Not tasks, not threads, but processes.
All that sounds like poor app design.

Microsoft doesn't force you to use SQL Server. I used Postgres and Redis for a quite big microservice based app.

Repository isn't mandatory, however is nice to separate the business logic from the details of the data store or database. Using a repository shouldn't be a a problem unless you are doing it wrong. I've seen enough of poor repository pattern implementation. I would question the skills of the developers here.

LINQ was not used properly if you had mixed IEnumerable and IQueryable based queries. It's not frameworks fault here, I'm afraid.

The system is not spawning threads for async requests, it's just creating Tasks which cost very little since they aren't physical threads. Think of goroutines, not threads.

All the things you mention seem to be due to lack of experience. I am not sure how using another platform would have yield better results, skill sets remaining the same.

It's not a problem if you try a new language, platform or framework as long as you don't just churn out code assembling method calls based on name (. NET makes this very easy because of intellisense) and examples found on Stack Overflow. As long as you understand what the code does, you are fine.

So the problem is that I'm using Microsoft's recommendations (Sql Server, repository pattern, LINQ) for a Microsoft product, and the difference between an inexperienced team and an experienced team is that the experienced team knows what list of recommendations coming from Microsoft are actually the ones you should follow in order to bypass most of the footguns.

I think I'm going to go with frameworks that have the actual best reccomendations baked in, instead of the frameworks that recommend footguns.

>I think I'm going to go with frameworks that have the actual best reccomendations baked in, instead of the frameworks that recommend footguns.

For every good framework, regardless of platform and language, there is going to be a learning curve. That's because flexibility and extensibility add complexity.

Only a trivial framework with limited use cases would not allow you to make mistakes. I take it you never used C or C+=, because there's a footgun waiting for you at every keystroke. :)

It’s interesting to look at this from the perspective of an experienced .NET developer, because you’re not wrong:

* SQL Server can be incredibly fast with good access patterns, but the rest suggests you didn’t have those. * Yeah, the repository pattern is garbage. I prefer just define the operations you need and use Dapper. * I know some on here are wild about Entity Framework, but personally I’d rather write the SQL myself. You need a good mental model of what’s going on under the hood or what you’ve described happens. * Honestly never used the default test runner because it seems pretty bad. NCrunch, however, is incredible and doesn’t suffer from this. * I don’t think I’ve ever worked anywhere that used the default logging implementation and yes, that format is weird. (You can, however, step into the logging code if you want.) * Depends on what you’re doing, but yeah 10,000,000 Task.Runs is not going to perform well. But I’m not sure the equivalent code works well anywhere except possibly BEAM.

So whilst none of these things cause me problems on a day-to-day basis, I can 100% see how inexperienced devs could drive straight into that tar pit.

Like you, I also wish HN would preserve newlines or keep asterisks as bullet points on newlines! :D
> LINQ supports interleaving DB operations with not-db operations, quickly becoming a O(n^m) footguns.

This is no longer the case since EF Core 3.0. Only top level projection can contain expressions that aren’t translatable to SQL.

> You better not do some type of async stuff in LINQ, because the system is going to spawn a process for each entry…

I wonder exactly what you ran into. This is not an accurate description of how EF Core’s async support works. Not even if you replace the word “process” with the word “thread”.

Usually in these situations I switch the junior developer to the back end which often increases velocity by 20%. Then let one of the senior devs take a brief vacation since they can be having issues at home. Usually by the time they are back, everything flows smoothly, velocity doubles and clients are pleased. I believe this approach can be tweaked to work even when the back end is done in .NET.
Funny how differently the grandparent's anecdotic but positive comment and this anecdotic but negative comment are treated.
For one thing, the negative comment appears to be claiming that the backend being more complex than the front end is a very strong indicator that the backend tooling was worse, and that in a web project, everything else equal, the front end should always take longer than the back end.

The entire premise is dubious, and even if the premise is correct, blaming it on the framework without any other evidence seems like a stretch.

Aye, I noticed this too and I’ve never written a line of production C# in my life.

On the projects I’ve worked on recently, the backend often ended up far more complex than the frontend, taking up more time to build and maintain.

This is natural. The backends were responsible for interfacing with other systems, like databases, 3rd party APIs and message queues, for example.

The frontends, on the other hand, generally interfaced with only one system: the backend.

Am I arguing that the frontend is never more complex and time consuming than the backend? Not at all.

But I don’t think you can blame the backend stack and tooling for the resultant imbalance of complexity between the two systems.

Because the issues described downstream are bordering on ridiculous and betray lack of knowledge of how to use the ecosystem rather than a problem with the stack itself.
I commissioned a house. The guy who did the paint job finished faster than the two guys who laid the bricks and did everything else. It clearly means that the bricks are of poor quality, I will never buy the same bricks again.
I commisioned another house. I have been building houses for the last 5 years. During these years I have seen that the bricklayers tend to take around 20% less time than the painters, furniture-installers and decorators. Somehow, in this project, with bricks provided by BigCorp the bricklayers are significantly late, and are delaying the whole project. It doesn't clearly mean anything other than I'm not willing to risk another late house by using again the bricks from BigCorp
Do you mean net for desktop applications or asp net?
Both. Definitely one of the best options for (windows) desktop apps.

And asp.net is fantastic for webapps, especially now that Blazor has become so capable.

The upcoming MAUI framework is also encouraging and should make cross platform native UI development much nicer.

Can you comment on how Blazor has become capable? It seems like a cool tech, but when I tried the wasm runtime about 6 months back it was still quite slow. I would like to use it for web app development but speed still seems to be holding it back.
In typical MS tradition, there is a naming issue. Blazor can mean client-side (via WASM) or server-side.
I can see why this is named this way, considering both variants share a lot in common, aside from some platform specific considerations (parallelism, and file system, etc.) moving from one platform to other is fairly simple. In fact back when Blazor WASM didn't provide decent stack trace on a browser console, I was doing development on Blazor Server and then ported it over to WASM.
Still, it is not Angular level slow while being easier to use.
While your gut may be telling you that, no actual data bears that out as a reality. Exhibit A: a set of benchmarks with actual rationale and cited sources: https://www.diva-portal.org/smash/get/diva2:1578257/FULLTEXT...

From an objective standpoint it's not a valid comparison. Blazor still has a number of fundamental deficiencies in regards to how it interfaces with the DOM event model via its messaging abstraction. You can't create a symmetrical set of tests without dropping concerns Blazor can't accomplish without custom JS, or limiting the scope of comparisons to what Blazor can do.

Do you mean speed in development or performance? .NET 6 has made major improvements on both fronts.

The best answer I have is the recent Blazor presentation from Steve Sanderson (the original author) from NDC Oslo conference on 2/21/2022: https://www.youtube.com/watch?v=Rn8psTi8FBk

It's 1 hour and will explain in far more detail exactly how good it is, from hot-reload (which is massive), importing other packages (including native Rust or C source like SQLite), unified server/wasm APIs, prerendering/hydration improvements, EF Core wasm, and even integration with existing JS react/vue components.

It is still quite slow, especially at first load due to its comparatively massive payload size. Server-side depends entirely on the connection speed of the interfaces and hardware involved, and will always be necessarily verbose.
The other mode of Blazor, server side, is where Blazor really shines. Performance is very fast on the client, and it's a much simpler experience for development that traditional SPA while having a very similar programming model. It's simpler partly because of the cohesive much simplified stack, and partly because for most simple one client apps you don't need to create a server API with all the extra work and issues that brings vs having your server side data in process.

Blazor WASM is still unfortunately slow on load, however the recent Steve Sanderson video on Blazor in .NET 6 showed impressive client runtime performance once loaded, by doing virtualized UI access to in process Sqlite for a truly native client performance much better than the current gen SPAs and getting back to how good software used to be, literally instant scrolling over tens of thousands of records.

But Blazor server is really excellent, recommend it.

Are there any demos or samples of MAUI? Last I checked it was in a very early stage and I couldn't find anything
I'd say that Asp.Net Core is a totally different from original Asp.Net. It is architected and implemented so well I still am amazed sometimes after working with it for a couple of years now.
I left .NET around 10 years ago, and have yet to be as productive in any other ecosystem.
JavaScript is productive once you stop listening to all the high level abstracts and complications. Vanilla JavaScript is beautiful and highly productive. Right up there with c# for server side ops.
There does seem to be some cultural inertia from the gap between when people made all the new frameworks and extensions to make JavaScript keep up with the advance of the web and when JavaScript got most of that functionality built in. CSS has the same issue where people don't realize it does most of what frameworks were made for originally like grids and changes based on medium/resolution/etc.
I think it's the curse of web development. It's so hard to keep up with all the brand new technology changes, so once you know the basics well enough you rarely take much time to look back at them. And Googling "how to create a grid" feels kind of redundant if you've already found a reasonable effective way to do it.
All these new features are nice, but not all users are using the latest browsers, so we have to keep falling back on these frameworks
The only scenarios I can think of where someone hasn't been on an auto-updating browser for 10+ years is embedded systems and locked down intranets where no one expects the latest and greatest, and people in circumstances where a minimal page not top-heavy with scripts and frameworks is best.
Have you ever worked for a bank?
As someone who has only written Node/TypeScript and C# backends in the last 10 years, I couldn't disagree more.

Node has so few "batteries included" that you spend a ridiculous amount of time searching npm.

That's especially bad because Node's ecosystem is a graveyard. There are 3-10 libraries for everything, and (if you're lucky) one will be well-tested and maintained. And even then, you're very rarely going to find something that's also: 1) maintained by more than one person, 2) used in production by well-funded developers, and 3) able to keep up with their Github bug reports.

In some cases, I have to try 2-3 libraries before picking one and often switch to another one later.

.NET, on the other hand, has a great ecosystem, batteries included, and (if needed) usually a commercial library for the really important stuff. It's just a far more stable and serious ecosystem in general.

Vanilla Javascript is productive until you reach a certain size of the project. After that the bugs will kill you. You will lose more time hunting bugs then writing code.

And that is not Javascript's fault because it was never intended to be anything else that a small script language to interact with DOM in a web page and add a bit of interactivity to HTML.

Me too I had Java envy, but now I realize a choice of 20 different frameworks doesn't make anything easier.
Well, for plain old websites Ruby on Rails I think is on par with ASP.NET MVC.

For Web API maybe Go is a reasonable contender if you put up with the boiler plate and verbosity.

As for mobile apps, I didn't encounter anything as productive as Xamarin while being reasonably multiplatform.

I didn't do desktop apps since ages but I remember QT and Embarcadero's C++ builder as being quite productive.

But one platform to do it all? There isn't other. Javascript is today used everywhere but I wouldn't quite qualify it as productive or performant. And countless frameworks and libraries competing with each other kind of makes it a mess.

Agreed, the versatility, as a mostly first class language across domains is awesome!

On the RoR comparison, while coding speed is similar, Blazor is an advancement, and then the runtime perf of .Net 6 vs RoR means having less servers, less cost, and less scaling challenges!

Techempower round 20 has ASP.NET at 309,458, compared to RoR at 8,260. That is 37 TIMES faster for .NET. Think of that as 37 times as much AWS bill for your RoR startup ..

https://www.techempower.com/benchmarks/

I stopped trying to convince others, once they saw the productivity and solid apps we were shipping they cloned our repos and are now .net devs.

Push they’re buttons all the time calling them Microsoft fanboys… that will get them going. Secretly they like it.

Extremely similar background here, similar feelings after working with .NET / modern C#. I even, semi- subconsciously perhaps, wanted to dislike it -- because I thought it was MS-ish, and ASP-ish. But really, it's a first class citizen and especially so the last few years as it's become very fast & received a ton of cross-platform support.

Once over the [quite low IMO] initial 'hurdle', it's easily amongst the most productive & best-supported "platforms" I've ever worked within.

I'm using ASP.Net Core for a couple personal projects for the first time, and it's pretty decent. I'm using nginx as a proxy, and I'm able to develop and test everything on Windows, and publish directly to a Linux test server. I have a lot to learn, especially for handling some web requests at a lower level, but I've been pretty impressed so far.
I recently used kestrel as a public facing server and it feels goods.
I'm not against it, I just happened to also need lots of nginx functionality...but a cursory glance does suggest YARP provides at least most of what I need.
Can you explain this a bit? I'm trying to figure out what the appeal is of YARP, so far the only thing that makes sense is the load balancing.
The big appeal for me is that with YARP you are treating your proxy as code that you write yourself.

YARP gives you all of the building blocks you need to build a simple proxy just as easily as a highly complex one.

Entity Framework owes a lot to nhibernate - but linq is the best thing that ever happened in ORM world.
Maybe full on EF/EFCore is better, but I know with basic Linq to Sql I often needed to check the generated SQL because sometimes it was non-intuitive how to get it to generate the simplest form possible (my biggest example was to do a 3 part join I had to list the tables backwards from the way I expected, doing the other way lead to a weird bunch of subselects, though note this is also in Framework so maybe they simply fixed the SQL compiler in Core).
I’ve been working with efcore lately and it has so far almost always produced what I’d expect, at least for my arbitrarily complex joins and left joins, both join calls and Include calls (and include simplifies a ridiculous amount of cases)

They also removed the intermixing between server logic and db logic, which greatly reduces nonsense occurring

LINQ is very hard to debug, especially depending on the provider. With anything remotely complicated it becomes write-once (like Perl :p)
Interesting. I’m a rails developer, and to me one of the core strength of rails is active record (altho some might disagree). How would you compare it with rails active record, and which part do you say it’s much better?

Btw, I know this is superfluous and very shallow, but for some reason, I can’t get over the C# syntax because of how enterprisey it is. I much prefer ruby, but I guess it’s a matter of preferences and taste

What the hell does enterprisey even mean .
Enterprisey usually means high-overhead, tuned to require many developers to achieve what would be done by one on a less enterprisey system.

This quality is attractive to managers who benefit by increased headcount, and prefer to have their "reports" or "resources" less generally capable, and more easily replaced or shuffled. Total cost is much higher, but individuals' salaries are lower, which makes managers look good.

Java and .Net are both very enterprisey platforms.

> high-overhead

You understand that all the things done in Java and .NET’s “enterprisy” way can be done is most languages right?

This is a programmer problem not a programming language one. Today you have to write less in C# than GoLang, to do the same tasks. Does that mean GoLang is enterprisey now, just because you have to write more?

Not "just because". Go is exactly as enterprisey as Java and C#, and for exactly all the same reasons.

And, it is not a programmer problem. It is a management problem. None of them would exist without management demand for minimally-productive languages.

What a weird hill to die on. I have a hard time believing “management” woke up one day and said: “how can we make the most minimally productive language”.

Tell me about this secret non “enterprisey” stack you are using.

Management does not make languages. They use what exists that satisfies their need for large head count. Sun and Microsoft were happy to provide it for them.
Again tell me what you think is not enterprisey then. I would like to open my mind to it.
i really liked the way ROM worked in ruby before i switched away from rails. I never got a chance to give it a good try.
ahem Have you considered F#, which could hardly be accused of being enterprisey?
Latest techempower benchmarks show ASP.NET Core being 37 TIMES as fast at runtime as RoR.

Think of that as 37 times as much AWS bill for your projects in RoR.. what were you saying about the syntax ¯\_(ツ)_/¯

Interesting! I’ve recently made a switch from C# to TypeScript and I’ve found prisma to be such a relief from working with entity framework.

Not that entity is bad in any way, it’s just not as productive. Which is sort of how I have it with most of the journey into TypeScript. It’s just a really great environment especially in Azure Function apps, and a massive cadeau to Microsoft for making the node in docker such a first class citizen in their Azure environment!

In many ways I feel like TypeScript is a mix of the best of Python and C# and that’s just wonderful, for me.

But writing things like Odata APIs in .Net sure has some benefits from the massive work Microsoft has done with the .net and C# environment since moving core into the main .net or however you describe it.

So now I’m not sure where I am going with this, I’m probably just very appreciative of the work Microsoft has done for us enterprise developers in non-tech enterprise in the past few years.

Prisma's query syntax feels quite verbose to construct and reminds me of CAML from my SharePoint days (except JSON vs XML).

It's also lacking support for a lot of data types on PG.

It's crazy that EF Core has providers for everything from SQL Server to CosmosDB to Google Spanner to in memory DBs.

LINQ and expression trees are probably one of the best features of .NET.

>Entity framework is one of the best ORMs

Yes, I think it is the best ORM in terms of productivity, but it is very, very slow. This is usually not a concern for internal enterprise applications that only have a few people using them at a time though. I guess the other issue is just an issue with ORMs in general - you lose a lot of the in-built features of your sql database, which as projects progress, can ultimately result in you writing the same amount of code as if you didn't use the ORM.

You also can't switch programming languages without rewriting the entire database section. Personally, I stopped using ORMs altogether because I find that they make easy things a little easier, and hard things harder, which isn't a very good value proposition overall. They don't really save me time anymore.

EF Core is not slow at all, especially if you disable change tracking, which you don't want in many cases anyway.
No it is definitely very slow. Stackoverflow did a test once, and switched to their light-weight ORM. The thing is, you mention disabling change tracking, and then there are things like having to make sure you aren't accessing a field that is outside of the included fields to prevent multiple queries, etc, etc. When I write SQL, I just write it once, and then it lives forever going very fast. When I look back on the projects written in EF/Linq, they are still going year after year, still using up lots of resources. I could have just written them in SQL. Maybe it would have taken an hour more over the entire project (internal enterprise thing), but it would still be running to this day, quickly and efficiently.
With ORMs I tend to write things with their native syntax, and then if necessary go back and speed up critical sections with SQL; it's not an either/or situation.
That was a long time ago. I believe it was based on EF and not on EF core. Is there any up to date benchmark that you are aware of? BTW I used both Dapper and EF for consumer based web applications with a mild load. Dapper is a nightmare to use compared to EF. Dapper was chosen by the customer architect as mandatory because probably of the same benchmark that you are referring to. The result has been that we had to write tons and tons of code to cover things that come out of the box in EF such as 1 to many relationships. Also every change in the schema forced us to review the full persistence layer to evaluate the impacts. I would reccomend Dapper only for websites dominated by SELECT queries on databases with simple DB schemas and only when some millisecond more make a difference. This wasn't absolutely the case for our application ( complicated DB schema and 5 ms vs 30ms spent in the ORM doesn't make any difference at all) while probably IT IS StackOverflow case. There is no such a thing as something slow or fast in absolute terms: it all depends on the context and as software engineers and even more architects we are called to judge with our brains everything in context rather than rely on a blog post written by somebody else.
>Stackoverflow did a test once

That was long time ago and EF improved a lot since then.

But it can be slow if used improperly. But hand written SQL can be slow too if not carefully crafted.

Any SQL query can be slow, especially if you have a large database and/or use a lot of Foreign Keys within your database.

Sometimes small changes in fragmentation, statistics or data composition will trigger a different queryplan which will affect your query immensely.

Good monitoring and a DBA nearby would be your best bets to overcome this. The point is that using a relational database is not a trivial thing once the database becomes large. Until then its relatively simple.

I don't think its a common opinion that its very slow. It builds queries and executes them very quickly compared to most.

It does have a built in footgun where for some structures it defaults to invisibly lazy loading sub-collections, you can (almost certainly should) disable that and specifically tell it to populate that data in the first query using Include() or any of the other projection mechanisms. Is it possible you had that?

Lazy loading has been disabled by default since EF Core came out.
The most comprehensive benchmarks to track are the TechEmpower benchmarks as they are open source so you can see what each framework is doing: https://github.com/TechEmpower/FrameworkBenchmarks/tree/mast...

The benchmarks model simple, but real world scenarios. It has breakdowns by EF, Dapper (the StackOverflow "micro-ORM"), MySQL, PG, and raw ADO.NET.

It should be noted that EF and EF Core are not the same thing; EF Core is more or less a full rewrite: https://docs.microsoft.com/en-us/ef/efcore-and-ef6/

Recent benchmarks have EF Core catching up to Dapper. You can read more about Microsoft's focus on performance for EF Core here: https://devblogs.microsoft.com/dotnet/announcing-entity-fram...

As others have noted, that's very outdated information.

The other thing is that for all ORMS, for the small amount of queries where it really matters, you should drop down to hand-written SQL anyway if the juice is worth the squeeeeze!

I am returning to Java ( Spring Boot, Spring Cloud and IntellJ ) after a long period with C# and .NET core. What a step backward it is! Really the .NET platform is leaps and bounds better than anything offered by Java today (at least when speaking about Spring).
.NET and C# as a platform are evolving extremely fast compared to many other runtimes and languages. In fact, it's kind of been converging with JavaScript and TypeScript. I have a small repo here that highlights this: https://github.com/CharlieDigital/js-ts-csharp

If you stopped working with .NET and C# around version 4, the language itself has transformed.

Local functions, pattern matching, records, and more!

And to be fair, it was always ahead of Java as a language. Reified generics in 2005, LINQ in 2008, Async, language servers etc etc.
If you can, you can give a try to Quarkus, while the doc is not up to the level of Spring, in term of ergonomics, configuration, hot reload, test containers, this is closer to what .NET does, by example annotations are optional and are resolved at compilation not at the execution.
> Entity framework is one of the best ORMs I've ever worked with.

It is good, but it's just better to learn TSQL/SQL Server if you are on a windows stack. Shouldn't use it as a crutch especially if you working on the back-end side. Entity framework was created to make data access easier for the front-end people.

Most of these are strawman setups, except for #6 (Boomer code) which is not a myth but a truth. I've spent a lot of years in the MS ecosystem and despite their attempt to entice many younger developers to .NET (and making the world more complex as a result) it's grey-haired devs like myself that have stayed loyal.
Zillions of young nerds out there are writing Unity games.
I don’t have any problem with the idea that .net is a fast, capable, modern language suitable for many purposes.

But I had to laugh at their example that “crushes” (why the embarrassing phrasing?) node.js.

Why, returning plain text is as simple as declaring a private “PlainTextActionResult” class that implements the IActionResult and the IActionResult method ExecuteResultAsync, creates an HttpContext.Result, sets the status code symbolically and the content type textually, manually calculates the content length, and returns an asyc Task object!

“Crushed it!” (with Enterprise-y code).

Whoever wrote that code either doesn't know the framework or perhaps is doing a custom implementation for some performance optimization. All you need to do to return a plain text response from an action method is `return Content("text goes here")`
Try building a serious app with node. Its a pain to build and maintain. In .net, you can have teams of people working on business logic DLLs, Data Access DLLs and have a few person team tie them all up in a web app. I hardly think that is possible in node.

All the enterprisey things in .NET are a god send in a 10k loc web app with solid debug and profiling capabilities.

And all that will save you time and money.

So many developers try to compare the structures of different stacks based on "hello worlds", whereas, the actual issues crop up in actual web development.

Why do you believe the same thing can't be achieved with packages in node that you feel is so useful for multi team orgs in your DLL scenario?
Well, I have.

With both .net and nodejs.

They both do the job fine (you mention 10K LOC, but I’m not sure that you mean because that’s still comfortably tiny — a one-person app at most, where pretty much anything works.)

In my experience they both scale just fine. Your limiting factors are much more defined by your app architecture, dev group politics (as reflected in response to management), and what I’ll call your dev workflow process (how many minutes is it, between when you as a dev make a final, correct source change, and the change makes it far enough that another person can see and evaluate it in the software?)

err, super easy to return text, if you haven't used .NET for a while, this is the minimum program to return text via the end point /test

  var app = WebApplication.CreateBuilder(args).Build();
  app.Map("test", () => "hi there");
  app.Run();
Of course.

So why the tortured code for the performance test?

Click through to the link to the code. BTW, the link is a screenshot of the code, but that doesn't happen to show the ugly part.

Could be shenanigans, could just be a minor stupidity. IDK, but it doesn't fill me with confidence in the article's general message.

From the looks, they just made a controller to return various mime types of responses, for the text they have made their own custom HTTP response, my guess is because there is no default serializer that produces text/plain (given there is no real standard for serializing into plain text)
What's not a myth is that they got rid of their JSON configuration in .NET Core in order to keep their XML abomination. I really wish they'd revisit that and at least allow both options.
At first I thought that to be a mistake also, but I gotta admit I don’t mind at all using xml in the new sdk-style csproj files now, it certainly made upgrading legacy project files slightly simpler.
the "abomination" is like 1% of its former size, i dont really understand what merits this name...
Modern .NET csproj/sln files are a few lines at most. As config files grow, XML is far better than JSON because it's much more structured (even if it has more verbosity).

Do you have a problem editing HTML? Because that's quite literally XML.

Some of the negative comments here illustrate how some cling strongly to old biases, despite that a lot has changed in .NET (and in Microsoft more broadly) in the past decade. I'm still bullish on .NET because I see the value it provides first hand and see the awesome things that teens and newcomers are making over in https://reddit.com/r/Unity3D . If there's one thing I wish I could convey to those who troll .NET, it's that no language is perfect, and each has its own unique strengths and weaknesses. It's not a zero sum game, and I think we all benefit from having different high-quality tools to choose from.
(comment deleted)
At a technical level, one of the biggest myths is that it doesn't require, or doesn't support, memory management. For high volume server-side development, not using "using" will cause resource and performance problems.
Not using "using" is an error tbh. But I think its wrong to call it memory management tbh, its about disposing anything NOT memory related (ie normal objects) like filestreams, sockets, custom things ...
It is memory management in a way. “Using” is releasing unmannaged memory most of the time.
It's releasing OS handles most of the time, actually. And you'll run out of those much faster than out of memory.
No, thank you.
(comment deleted)
Hard disagree on the tooling costs. VS Enterprise is $250/month per seat. VS Code is way too limited for most .Net projects.

…and that’s if you can even figure out how to manage your VS licensing within the deep abomination that is the MS sign-on and management. Most frustrating experience of my life entire professional life was dealing with O365, Visual Studio, and Azure subscriptions.

$250 is several hours of most developers pay. If it saves them any amount of time it's incredibly easy to justify.

It's also rare to need what Enterprise has that Professional does not.

It doesn't. It wastes time and it's only used because your stack is built on EF6 database first and it's the only method to update your ORM entities
That's an extremely aggressive response for an entirely subjective viewpoint.
Why do you need VS Enterprise? Professional is enough and costs $45 / month
Community edition is the same version as pro and cost zero.
Rider is $349/year
Note this is the organization price. If you are buying for individual it is $149 or you can get the full suite for $249 (going down $50/year for years 2 and then 3+ stays at $149)
Rider is also a good piece of software. VS is garbage
Have you tried VS2022, which is basically a complete rewrite? In my limited experience, it's dramatically better than VS2019.
(comment deleted)
>VS Code is way too limited for most .Net projects.

I've been forcing myself to use VS Code for my last few C# projects and it's actually quite usable once you get used to the quirks and limitations. The biggest issues:

1) You really need to understand the dotnet CLI tools or you're going to be in for a world of hurt

2) Omnisharp craps the bed way too often. Restarting it from the command palette fixes 99% of the issues though.

3) You need to know the ins and outs of launch.json and task.json in VS Code if you want to do anything more complicated than build and attach. Full VS is much easier to work with here.

> Omnisharp craps the bed way too often.

FWIW I've had a much better experience with OmniSharp since they shipped a version running on .NET 6 (opt-in with the "omnisharp.useModernNet" setting). YMMV but I need to restart it much less now. https://www.strathweb.com/2022/01/hello-omnisharp-on-net-6-0...

Yeah I'm running the .NET 6 version of Omnisharp and it's definitely a major improvement, but it's still got a ways to go.
I did the same thing and hard forced myself to code in VS Code and now I'm fully comfortable working in VS Code on macOS.

The main stumbling blocks are really around debugging setup. A lot of stuff that VS IDE proper sets up for you automatically you have to manually wire up in VS Code.

(comment deleted)
After giving the article a few reads it seems that the points are a bit cherry-picked...

> Myth 1: .NET is for Windows

It depends what you mean by .NET, doesn't it? In-ecosystem a .NET-enjoyer might be thinking about .NET Core, and thinking about building a nice new web API service. But besides that specific case, it still is all just Windows. And I'm not talking about API and ABI dependencies or hard-coded win32 assumptions, I'm talking about making a first-party GUI Desktop application or an application that works together with the OS, natively. And no, until MS dogfoods MAUI in production for a few years, that isn't the silver lining it seems to be (just like SwiftUI doesn't help at all if you're trying to escape NIBs and XIBs - same problems, different flavour).

None of this should come as a surprise, Microsoft does after all still make a separation between 'free' stuff with C# and 'making money' with C#, where the latter is designed as a funnel towards buying something from Microsoft and making sure that it works with Microsoft's stuff (which is: Windows). This isn't a "haha .NET bad" thing, it's just a reality. The same goes for the .NET Foundation. It's essentially still just a shell, controlled entirely by Microsoft. Again, no surprise, but a bit dishonest at the very least.

> Myth 2: It’s Slower than Node/Python/Go/Rust

A few examples where it can be marginally different (faster and slower) but nothing extreme that would make it 'the best choice'. This whole "my language is better because performance metric X" thing might make sense if we're talking orders of magnitude, but we're not (mostly). Also, the bust points out bandwidth, but here is a surprise: bandwidth matters a lot less when it's not the amount of bytes you need to have flowing, but the latency at which the flow starts.

> Myth 3: It’s a Legacy Platform

It's not a legacy platform because you can build new things with it, it's a legacy platform because everything .NET that is already out there is largely legacy. Ironically, this can work out both ways, since well-written C programs can both be legacy and current at the same time. But for .NET, the problem lies with the rather recent pseudo-community opening up and not with only the very last most recent .NET Core release.

> Myth 4: The Tooling is Expensive

It is still expensive because the good tools are always expensive. That is not always a bad thing, if you make money, good tools can be worth their money. But pretending that all tools are the same and therefore the expensive ones don't count as "the tooling is expensive" is not really honest. Again, if you were to make a new project, in isolation, targeting building a self-contained CLI application or API server, you can do that with any text editor. But we're not really exclusively writing code for those, are we? (more on this later)

> Myth 5: .NET Isn’t Open Source Friendly

It's looking friendly but is also very dishonest. See earlier text about the foundation (and earlier posts in HN about the same issue).

> Myth 6: It’s for Boomer Enterprise Development

Not sure how this myth came to be. Existing long-lived applications definitely start entering the Boomer Enterprise Development zone, but that applies to pretty much any language and framework that's been around for a while. See rant about greenfield development and the reality of existing software above.

Now, don't get me wrong, none of this makes .NET explicitly good or bad, but it does smell like a "we are really good, why don't you love us" type of Myth-post. When you start a new, non-Desktop, non-GUI application with proven technologies, then .NET works fine, just like Go, Python, NodeJS and even Java. But in that same sentence, a problem pops up: it always irks me personally that you apparently can't say C# on its own. It's always C#, wit...

Early ASP.net was a fairly awful experience for the web stuff I did. ASP.net 1 and it was missing a lot of basic functionality that came in .net 2 and was not compatible with the stuff we had created in .net 1 to fill the gaps. Visual Studio was fragile, it would work great one day/week, then one system update or one change to a project setting would be enough to make it crawl or crash. And to top it off we had the burden is having to use visual source safe as well. Then add the need to have a local IIS and SQL Server, required a seriously beefy spec desktop to run. The ASP.net viewstate was geared up for single page CRUD applications and trying to do any kind of multiple website required serious abuse of session storage (DB backed and hideously slow to serialise and unserialise each time) to maintain state across pages. We created a handful of helpers to utilise querystrings efficiently. ASP form validators often suffered edge case bugs, the javascript validations often fail and hard to debug, server side validation added an extra layer of slowness and trying to juggle everything to load, save, validate, update the html between the layers of state management (application, session, view etc) lead to some horribly complex code to work around the opaque-ness of asp.net state management. Entity framework was welcomed, but it was almost unusable to start with it as suffered critical inconstancies between 32bit and 64bit machines in the way it handled the length of integers and stored them in the EF defined databases.

But it was not all terrible, C# was a pleasant language, polymorphism useful, unit testing (nunit) worked well, Team Foundation Server is worth a mention for being a zillion times better than Source Safe. Linq was a sane abstraction. By 2007/2008 MVC looked promising and Visual Studio Pro ran quite reasonable on current spec machines and the things around .net such as SQL Server and IIS were far more pleasant to install and work with on desktop machines.

PS, Let's not forget the MSDN library, documentation and software on CD/DVD and having to strategize what you can fit on your HDD and what you will load from disk.