87 comments

[ 1.9 ms ] story [ 285 ms ] thread
NativeAOT is one of the most important feature C# needed, very nice that it is finally coming, i still can't believe it took them this long

Congrats to everyone who fought vigorously to make it happen, screw the managers, execs and their surrounding noise, long live to the real engineers at Microsoft

Agreed, I feel like it was the missing piece from their .NET Anywhere story and am curious to see if it creates an uptick in adoption
This was the first thing I tried out this morning and apparently it is not meant to work with ASP.NET (Web API in this case) and only for console applications.

Explains why my container got stuck packaging Kestrel this morning.

Oh that’s disappointing. Looking through the blog post now it specifies console apps now - wonder if Web API support is coming later.
The article mentions things like:

  - "All required code is compiled and/or linked into the executable, ..."
  - "No JIT means no dynamic loading of arbitrary assemblies ..."
  - "... with everything compiled and linked into the app ..."
This sounds very much like static linking, possibly with no option of dynamic linking/loading of code.

Presumably this would have implications for those using LGPL-licensed .NET libraries, especially those who'd like to distributed closed-source applications AOT-compiled using this approach?

Are there any commonly-used .NET libraries these days that are only LGPL-licensed?

I haven't done .NET development in a decade, so I don't really know the current state of affairs.

Is there a difference between static or dynamic linking for LGPL?
Yes. The point of LGPL in concept is there is a clear boundary where you can modify and replace code. You can't replace a statically linked library.
Yes. Because dynamic linking happens at runtime, the DLL/so file can be replaced by a downstream user. Static linking does not allow the user access to that "freedom", so the whole binary becomes "infected" and must be open sourced. By enforcing a "user replaceable" boundary, the users' freedoms/rights to modify the open-source library are unaffected.

Side note: how does that fit in with cryptographic signatures? GPL v3 was written to combat "Tivoization" (open source, but can't replace the binary). Does the LGPL v3 disallow such things with DLL/so files (use hashes to prevent replacing the FOSS lib)?

Straight from the LGPLv3: "A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version."
The LGPL's boundary between your application and the licensed library requires that the end user have some way of substituting the LGPL library. So if your project is not at least source-available enough for the end user to recompile it with a modified version of the LGPL library, you're going to have a hard time meeting that requirement with static linking. More common is to just dynamically link to the LGPL library, which provides the substitution mechanism by its very nature.
I have used NativeAOT before, and it does not enforce static linking. "no dynamic loading of arbitrary assemblies" refers to .NET assemblies, not the native dynamic link libraries they are compiled (using NativeAOT) to
While NativeAOT is to be praised, we should not forget all the avenues that came before it.

NGEN, Singularity and Midori research (whose tech landed on Windows 8 MDIL and UWP .NET Native respectively), Mono AOT.

I always were the opinion that given Anders Hejlsberg background, .NET 1.0 should have had a better AOT story than NGEN.

They really need to extend the support time for their LTS releases. Sure, if you're developing a SaaS you can stay on top of .NET versions but it's definitely causing friction for enterprise and slow moving regulated software. .NET Framework 4.5.2 was supported for 8 years in comparison.

We're about to start a project with those limitations and there really is no good choice now that .NET 6 is already a year through it's lifecycle.

If, when publishing your app, you choose the "self-contained" mode (as opposed to "framework dependent")[a], the runtime will be bundled with your program into the executable. Then you don't need to worry about updating your client's OS. My work is using it for deployment to Linux, and it works great.

[a]: Pass the `--sc true` option to `dotnet publish`: https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-p...

Bundling the framework doesn't solve the problem of said framework being old and unsupported because it wasn't LTS.
Seems like it makes the problem worse because you are then tied to a specific version at time of publishing and need to republish to get any framework patches.

https://learn.microsoft.com/en-us/dotnet/core/deploying/runt...

Indeed. Microsoft identified this as a problem as well and pushes .NET security patches via Windows Update nowadays. When using self-contained apps, you won't get these patches.
.NET Core security patches aren't pushed via Windows Update, only .NET Framework (since it's built into Windows). Unless something changed recently?
You missed how there effectively isn't a .NET Core/.NET Framework delination anymore. It is just .NET, and .NET gets security patches from Windows Update.
.NET Core got renamed into .NET as of .NET 5, and it is still quite relevant to make a difference, plenty of people still don't get it.
It's same issue as deploying containers.
> Sure, if you're developing a SaaS you can stay on top of .NET versions but it's definitely causing friction for enterprise and slow moving regulated software.

You have a point, but I'd call out that migrating to newer releases can and often is trouble-free.

A couple of months ago I worked on a .NET Core 2 web service that was put on cold storage for a few years, and all it took to migrate it to .NET Core 6 was a few version number bumps and a rebuilding the project.

YMMV of course, but LTS shelf life might not be a deal breaker.

You may have gotten particularly lucky.

I say particularly, because Core2->Core3.1 was a fairly painful change for some.

I'd say the 66% worst case is 'you also have to add some compat flags and stories to properly resolve the compat flags'.

And web forms to core was/is terrible.
It took almost two years of development to do this conversions in out project... but in my case heavy lift of move from webforms to mvc were already done, so it a bit easier.
Slow moving orgs with brittle release processes are going to miss support deadlines anyway, it doesn’t matter if they arrive sooner or later.

Until the date hits them in the face there’s no urgency.

Unfortunately in some industries continuous delivery is literally illegal. Anyway there's a real issue with their current schedules. Let's say you're developing software some time in the middle of next year. There's effectively no LTS release available at all.
We're not talking about "continuous delivery", this is just having a plan to ship updates every two years, on a well defined schedule. If you can't manage that organizationally, there are deeper problems.

It's not really Microsoft's job to work around your awkward requirements. Everything is open source, you can ship patches for .NET 6 yourself if you really want to.

The answer is to fix your processes, not demand Microsoft accommodate them. The lifecycles are already plenty long.
My issue is the cadences are difficult to work with.

Microsoft's general-ish LTS for .NET is 2 years. We also only seem to get a new release every 2 years. It's hard to handle breaking changes between two versions quickly.

LTS for .NET is 3 years, not two. There's a new LTS every 2 years, so you get a full year to move from LTS -> LTS if you need to.

What kind of breaking changes have been hard to handle?

Any way you look at it 3 years is pretty low for industry standard LTS lifecycles. Besides the .NET Framework comparison Java LTS is 5-8 years.
Node (used at least as much as .NET and Java in enterprise) has a 3 year LTS cycle, so I don't know if it's really low for an industry standard.

    Node (used at least as much as .NET and Java in enterprise) has a 3 year LTS cycle
I'm gonna need a citation on that. From what I've seen, what enterprises have done any node adoption have fled it like rats fleeing a sinking ship because of all the problems it's had over the past half decade. To the point that shops I've worked with are moving back to Java.
I agree that LTS should last longer. They made it easier to upgrade between versions though. Perhaps won't help you if you use .NET in patient critical medtech for instance (in EU).

Biggest problem for us is .NET Standard. I don't even understand how it could work on a theoretical level. Maybe it could work if there was no dependencies.

.NET Standard is for libraries only.
I know what it is. It is just broken. App A uses a .NET Standard lib B that needs lib C. Since the purpose of lib B is to be as compliant as possible it targets some .NET Standard 1.x. Lib C is used already by the app. And now, dependency hell.
Binding redirects should solve that issue.
Sometimes. But most .NET devs don't even know what a binding redirect is. They will also break on a regular basis when packages are updated.

Edit: The bindings that is. Maybe the devs too.

I guess I am missing something, why does this cause dependency hell? I guess microsoft is basically recommending against targeting netstandard now anyways so there must be some problem with it. But I don't think I have run into any problems with the situation you described.

edit: Ok I see, it seems in that scenario loading the correct assembly is more difficult on dotnet framework and usually requires binding redirects to be specified. On dotnet core/.NET 5+ there are no issues though, which is why I haven't experienced this problem myself.

https://nickcraver.com/blog/2020/02/11/binding-redirects/#th...

It has never really worked between different .NET versions. Nothing new with .NET 5/6/7.

We pretty much banned .NET Standard for any in-house dev.

What limitations are involved? I’ve never really understood why 3 years is unacceptable or intractable. In nearly every case the real issue is that nobody wants to fix broken internal processes that prevent people from upgrading once a decade.
(comment deleted)
It has everything to do with creating a product that you sell to a customer who installs it on premise within their environment, and their IT policies require that software be supported for a certain period of time. Even if you're good at changing their mind some of the time, it's not going to happen every time, so you're basically turning away potential customers, which some people aren't willing to do.

This is why it's much better to move away from the shrink-wrap model of delivering software and focus on SaaS only solutions.

It's not always that the internal process is broken - it's that sometimes it takes a very long time to get a new release approved for use (even if it's an upgrade to one that the customer has already been using). Sometimes it's the customer's process that is slow (big enterprise culture), sometimes there are one or more third parties involved. Sometimes it's both.

Many years ago I wrote software for a medical device. Before we could sell the device we needed to get it tested by Underwriters Laboratories to ensure it was safe (wouldn't shock people) and could be cleaned by hospital staff without damage. We also had to do the process to get the CE mark for it so it could be sold in the EU. And of course the FDA needed to approve it. It took nearly a year for all this, before we could offer it for sale and start marketing it.

So there's a third of the LTS lifetime consumed, right there.

(comment deleted)
(comment deleted)
There isn't any functional difference between a 3 year or 5 year or 8 year support cycle. The only two development modes for a service are – build once and maintain occasionally, or build once and leave untouched forever. Those in the first set will be fine with 3 years of support. Those in the second will always ask for more time no matter what the deadline is.
It's not 3 years of support though, if you ship mid next year it's 1.5 with no alternative to choose.
There will be a new LTS version available for 1 out of those 1.5 years. Plenty of time to upgrade. Plus, the advantage of shorter release cycles is that there are fewer breaking changes from one version to next. As long as there is decent test coverage it could be as simple as updating the build target in your CI tooling and calling it a day.
We're in the process of migration a large MVC application from Framework 4.8 to .NET 6. It's been....painful. Hopefully at least for a while updating to new LTS versions will be less so.
.NET Core 2 to 3 definitely had this feeling that their vision for the framework wasn't complete. Going from 5 -> 6 -> is pretty painless.

We are also preparing for a move from .NET Framework 4.7.2 to .NET 6/7. It's going to be quite the project.

We did that last year on a 3 million line / 500 person-year investment application. Quite challenging as everything from app domains to remoting, to call contexts and other unsupported things were being used. It was definitely worth it for performance alone as we were also using Mono to run on Linux.
I am moving a personal project with ~50k loc and it has turned into a rewrite of about 2/3 of it. Not fun, but on the plus side the app runs A LOT faster now. So much faster that even non tech people that use it noticed the difference.
I love Java. And I'm working on c# right now at my job and I'm loving it too. This is happy news for me. When my enterprise finally adopts it 4 years from now. D:
I was chatting with the devs at my company and they've just migrated their code to .Net 6!
Upgrades are honestly really easy, and have been since around .NET Core 2. I have now done 2 -> 2.1 -> 2.2 -> 3 -> 3.1 -> 5 -> 6 with very little friction at all, in a fairly big web API codebase.

I don't foresee any issues going from 6 -> 7. 5 -> 6 was essentially just a find and replace on TargetFramework and it all just worked.

Ahh. I think it was quite an effort. The whole organisation really did spend some time and tested it thoroughly. I reckon it depends on the scale and complexity of the code base. I'm not a dev, I don't know enough. I'm worried about changes to Python packages breaking my code. haha.
So if I'm reading https://dotnet.microsoft.com/en-us/platform/support/policy/d... correctly, 7 is an STA release and support for it will end before support for 6 (LTS release) ends?!

Seems like the only reasons for upgrading from 6 to 7 would be an easier path (possibly) to upgrade later than going from 6 to 8, or if you really want to use something in c# 11 (https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/cs...) . I remain unconvinced...

I guess You need net7 of You really need every bit of performance.

In our case improved hit reload is very welcome. So I guess we will incremenet our runtime.

Could you tell me a bit more about improved hit reload?
Changed 6 to 7 in my project file, the program started running 10% slower ... hmm. Specifically,

5 1107ms

6 1075ms

7 1211ms

Just number crunching and calling HashSet.

If you are using `time` and calculate the time from application startup to exit 0 then it is not representative of performance of the code itself - more aggressive optimization in JIT tend to have increasing cost with each release even if the people working on it try to always ensure the compilation throughput stays constant and we get more optimized JIT code per same amount of CPU work.

If you would like to have representative benchmark data, I suggest relying on BenchmarkDotNet instead.

Looking further into it (I really want my program to run faster!), looks like 7 produces unoptimized code. Could it be that it decided for me that I want faster startup vs faster code? Can I force it to turn optimizations on somehow?
If you do only a couple of iterations it will not optimize it. Loops can be now optimized in-flight thanks to on-stack replacement feature which should help with startup time and also allow to apply Tier-1 compilation with more optimizations should the loop become hot (e.g. can't elide static initialization checks if you go straight to full optimized without Tier 0 -> Tier 1). To get more representative data it is best to either use BenchmarkDotNet or profile code close to real life case.
I see. So I just inserted <TieredCompilation>false</TieredCompilation> into project file and it fixed it. Runs as fast as 6.
Feel free to open an issue for this over at github.com/dotnet/runtime, if you have code you can share.
Please do not use this. JIT is designed with tiered compilation in mind for a long time. I cannot stress this enough, the number in the above comment is not representative. Using this setting is highly discouraged.

Please use BenchmarkDotNet to get accurate numbers!

my program's main purpose is not running benchmarks)
There is no doubt but if you would like to accurately compare a part of your code on 6 vs 7 then the best practice is to use BDN with [SimleJob(RuntimeMoniker.Net60)] and [SimleJob(RuntimeMoniker.Net70)]. Make sure to set project to multi-target both versions. That way you will see actual difference, feel free to also add [MemoryDiagnoser] and [DisassemblyDiagnoser(exportCombinedDisassemblyReport: true)].
Use benchmark.net for tests and come back