00:00:00 - Introduction to .NET 9: Overview of new features and their importance. 00:00:34 - Feature Switching: Explanation and implementation of Microsoft's built-in feature flags. 00:03:00 - LINQ Enhancements: New LINQ methods, including AggregateBy and CountBy. 00:07:00 - Spans and Memory Efficiency: Efficient use of ReadOnlySpan and ReadOnlySet. 00:16:44 - Hybrid Caching System: Demonstration of combining memory and distributed caching. 00:22:10 - JSON Schema Exporting: Exporting schemas for external integration and OpenAPI updates. 00:24:14 - Sequential GUIDs: Introduction of version 7 GUIDs for database and distributed systems.
The way Nick writes stuff in his project and when he opens them, just removes them without addressing them, or even smiling, makes me chuckle every time. "JASON!"
@@nickchapsas I love the video and your work. Wonderful stuff. I am even a PRO on Domtrain to support you! But it is more about how the the Dic variable is pronounced :-)
params was only legal with an array type, not even list, enumerable or the alike. So we also get that, and full support for spans, making the feature complete. Though I still reckon it useless now that we've got collection expressions, which were made for the sole purpose of making it more convenient to express a collection of elements.
Hi Nick, thanks for sharing this. Wondering if we can someday get a playlist on Span, because I want to learn it in-depth to master how and when to use it. Thanks in anticipation.
In my experience, a feature flag requires quite often a new IoC container. Feature disable => register null object Feature enabled => register real object That means that I have to perform a deployment, when I change the flag.
We implement feature flags via database settings. By doing it this way, we are able to update the flags value runtime and enable/disable features. With this implementation, we always need to deploy the application with the changed flags values. I haven't found a way around that. Do you have any suggestions?
I really tried to adopt HybridCache ahead. Although the flags is a great feature to control how items are handled, it is too simplistic. I really missed the plain Get(Async), and the ability to gracefully cancel adding something to the cache. The backplane is also something that is a must for L1+L2 caching IMHO... So for now HybridCahce is the way to go for me...
Do I understand correctly that the difference between a config.json and this is that the new feature is like a precompiler statement so it get removed entirely from the compiled code?
If the newer stuff in your database is more relevant, and you start using V7, then eventually you will get the benefits I believe. Maybe not as good as if all the guids were V7 but I think it will in the long run. I'm just guessing so take this comment with a grain of salt
If you are interested in HybridCache, you may take a look at FusionCache that Nick mentioned, too (shameless plug 😅). It has a very similar design (L1 MemoryCache + L2 IDistributedCache) but it has been around for years, battle-tested in production, is even used by Microsoft itself (eg: see Data API Builder) and has a ton of extra features, particularly around resiliency. Not posting the link to avoid bans, but it’s easy to find. Hope this helps.
Not really seeing the benefit of the new feature flag system. If it's all in memory, then how can I do A/B testing on it. Like, there needs to be a way to toggle the flag externally while the process is running, like feature flagging systems do today.
Delivering just the standard OpenApi document allows users to use whatever they want. So if one is more of fan of Postman, Insomnia, Rider, etc. you can just import that spec. The other thing that one should worry about is external dependencies, both "what" and "how many". Every external dependency you have could become a risk. For example it might die, get stale, become monetized, even becoming a security risk.
It completely removes the code. Reduces the evaluation by a single if check. Basically, the same as using a ConditionalAttribute w/ a compilation symbol.
Wait a second, how Feature Switching interact with all the previous ways to obtain feature flag? Like App Settings files? Or Feature Management? Azure App Configuration or other external feature flag management systems?
So... "Feature switching" was implemented in COBOL in the 60s and it was WAY simpler to use. Color me unimpressed by this feature. It's also redundant as you've been able to use arguments to do the same thing since probably .NET 1. And if you wanted to drop code out of the compiled output, you could use compiler switches and #ifdef.
FeatureSwitching is a strange mix of compile time and runtime. The project file settings getting built in and the stripping of the code for AOT has compile time behavior while the actual lookup from the static AppContext is runtime. They use that way to setup parameters for GarbageCollection etc. since NetCore and i kinda like choosing between a json and project file better than the old appConfig. In a way it is probably more convenient than building your own static config class or #ifdef but it doesn't seem very elegant to me.
Am I smoking something? LOL... In your first example, my first thought (before you changed the code to use the || operator), I thought you ought to default that to false, b/c if the TryGetFeature fails, you wouldn't want to default to TRUE... but anyway... then you converted the statement to use an OR for the result... but isn't the result of one of these TrySomething(...) calls the result of the "try"? Meaning that if it fails, it returns FALSE, and if it succeeds, it returns TRUE, and the actual result (assuming that the try succeeds), is whatever is output (in this case TRUE if the feature is enabled and FALSE if not)... so in that case, since the TRY always succeeds (e.g. TRUE), and you did a || isEnabled, the result of TRUE || anything is always going to be TRUE. Sorry to be so wordy... Maybe I missed something, because clearly your demo worked, but I'm just wondering what I failed to catch there.
basically if the feature switch can't be found the feature is on by default, otherwise use the configuraiton that was found in the settings. You can change that behavior however you like
I am still disappointed that params IEnumerable (or a collection expression converted to IEnumerable) constructs a hidden ReadOnlyArray behind the scenes from the array. Like why? An array is already an IEnumerable, and since it gets created too at that point, there is no code that cares about the elements after the call. Nothing except the target may observe any modifications, and not having access to the underlying array misses out on optimizations performed by the target. "params" or collection expressions should construct the strongest implementation of the interface (that being an array in the case of IEnumerable), not the weakest implementation. Just don't use params or collection expressions with interfaces.
Unions is still years off at this stage. It has only been proposed. They're still at the preliminary stage at having proposed an API. They still have to incorporate feedback, implement and validate prototypes, etc. But we still have nice packages in the meantime that can help cross the gap, e.g. OneOf.
Saw a few comments about why Nick was using 'decimal.Add' over the '+' operator so thought I'd step in to help clear up any confusion. You would think that these two ways would do exactly the same thing. Well if you read the official C# documentation you would find out that you are completely correct and the reason Nick uses 'decimal.Add' is cos he's a crazy muthafucka.
So the AggregateBy and CountBy allow us to do what we could already do easily and more readable as follows? from lesson in completedLessons group lesson by lesson.CourseId into g let totalWatchTime = g.Sum(lesson => lesson.LessonDurationSeconds) select $"Course id: {g.Key}: {totalWatchTime} seconds"; from lesson in completedLessons group lesson by lesson.LessonId into g select $"Lesson id: {g.Key}, {g.Count()} times"; Am I missing something?
Feel like he's a little too old to use sexual innuendos in his videos. What is this .NET for middle schoolers? I don't know if anybody actually finds this funny but it really makes him seem less credible and immature.
@nickchapsas it still doesn't sort right. You can sort a classic GUID, too, if you wish. Its going to be random order. You can see it from your own video that those 3 values would sort into wrong order.
@@GigAHerZ64you look at them as if they were strings and try to sort them lexicographically left to right. There's a specific bytes placement logic, some of them are even sorted in reverse. Try to sort 20 GUIDs, you'll see.
Thanks for the overview Nick. I still write C# at work, but personally I never create a new project in C#, when there is Go. Overall .NET 9 is a disappointment to me, there are some nice features, but it seems they are trying to please everyone, and lack a strong vision for the future of C#/dotnet. It's a perfectly capable and productive language, just not for me anymore. Thanks
00:00:00 - Introduction to .NET 9: Overview of new features and their importance.
00:00:34 - Feature Switching: Explanation and implementation of Microsoft's built-in feature flags.
00:03:00 - LINQ Enhancements: New LINQ methods, including AggregateBy and CountBy.
00:07:00 - Spans and Memory Efficiency: Efficient use of ReadOnlySpan and ReadOnlySet.
00:16:44 - Hybrid Caching System: Demonstration of combining memory and distributed caching.
00:22:10 - JSON Schema Exporting: Exporting schemas for external integration and OpenAPI updates.
00:24:14 - Sequential GUIDs: Introduction of version 7 GUIDs for database and distributed systems.
The way Nick writes stuff in his project and when he opens them, just removes them without addressing them, or even smiling, makes me chuckle every time. "JASON!"
The variable Dic really got me laughing :-)
it's all about that new dic
@@nickchapsas I love the video and your work. Wonderful stuff. I am even a PRO on Domtrain to support you! But it is more about how the the Dic variable is pronounced :-)
Dic, hello 69
Followed by hello 69
Try not to let your dic get too big. You want to keep it managed
LIKE this who wait video about EF Core changes
amazing summary in just 25min, thanks!
params was only legal with an array type, not even list, enumerable or the alike. So we also get that, and full support for spans, making the feature complete. Though I still reckon it useless now that we've got collection expressions, which were made for the sole purpose of making it more convenient to express a collection of elements.
We're doing decimal.Add() over + now?
Yeah, not sure the point. Why would that just be the implementation for the + operator?
Genuine question, why did you use the decimal.Add instead of just + the operands?
Hi Nick, thanks for sharing this.
Wondering if we can someday get a playlist on Span, because I want to learn it in-depth to master how and when to use it.
Thanks in anticipation.
Stone cold face when referencing the holy numbers. Impressive.
sacred integers
I wonder why they didn't add SumBy and AverageBy
07:17 Nick turns to a salesman for a couple of seconds, his tone, his character even his gesture changes x)
Noticed 😅
I didn't know there were people who existed on the internet without adblocks. Thats crazy!
@@fredrikjosefsson3373 Hahaha, I like my favorite UA-camrs to be paid 😂😂
Gotta make some bread
Thx for this high quality content nick 👌
Personally I liked the backing field keyword addition the most.
I dont really follow what benefit that feature flag has over using normal IOptions. Unless you want the AOT trimming that Nick mentions
A/b testing
Thank GOD for the discount code. I missed my chance the first time and flat out couldn’t afford it lol
Do you plan any Dometrain courses about .NET Aspire or/and GenAI with C#? This would be very helpful :)
Yes and yes
UP
Why is AggregateBy using decimals (instead of ints or longs). And why use decimal.Add instead of simply + ? Am I missing something?
I wonder if it's a meme or an internal joke.
There's no reason why you'd aggregate integers into a decimal, that's for sure.
Great Video, please video for Blazor!
In my experience, a feature flag requires quite often a new IoC container.
Feature disable => register null object
Feature enabled => register real object
That means that I have to perform a deployment, when I change the flag.
through wrapper classes, all things are possible
@ShinyBorel Yes, I could introduce a factory, or a Func, but I would carefully consider the pros and cons
We implement feature flags via database settings. By doing it this way, we are able to update the flags value runtime and enable/disable features.
With this implementation, we always need to deploy the application with the changed flags values. I haven't found a way around that. Do you have any suggestions?
I suppose that is the intended use case. So you can have your own internal features that never get shipped into production.
I really tried to adopt HybridCache ahead. Although the flags is a great feature to control how items are handled, it is too simplistic. I really missed the plain Get(Async), and the ability to gracefully cancel adding something to the cache. The backplane is also something that is a must for L1+L2 caching IMHO... So for now HybridCahce is the way to go for me...
Having mentioned the backplane I assume you are using FusionCache now, glad you’re liking it (creator here).
Do I understand correctly that the difference between a config.json and this is that the new feature is like a precompiler statement so it get removed entirely from the compiled code?
"some number" 🤭
MORE SPANS!
More about ASP .Net Core and EF
Glad you mentioned the "meh" part
Dude had a lil' Heavy Rain reference in there :D
PRESS X TO JASON
Can the V7 Guids replace the old Guid in an existing application or you have to start with it in order to see it's benefits?
If the newer stuff in your database is more relevant, and you start using V7, then eventually you will get the benefits I believe. Maybe not as good as if all the guids were V7 but I think it will in the long run. I'm just guessing so take this comment with a grain of salt
Dic is back! :D
I don't understand why AggregateBy was introduced: it's just a one line extension method.
And then why only CountBy?
They’re probably slow rolling `HybridCache` because stampede protection feels like such big deal. Gotta be sure it works.
If you are interested in HybridCache, you may take a look at FusionCache that Nick mentioned, too (shameless plug 😅).
It has a very similar design (L1 MemoryCache + L2 IDistributedCache) but it has been around for years, battle-tested in production, is even used by Microsoft itself (eg: see Data API Builder) and has a ton of extra features, particularly around resiliency.
Not posting the link to avoid bans, but it’s easy to find.
Hope this helps.
The weather is the same within like 5 minutes unless you live in London or something 🤣
Came here looking for this comment 😆
Not really seeing the benefit of the new feature flag system. If it's all in memory, then how can I do A/B testing on it. Like, there needs to be a way to toggle the flag externally while the process is running, like feature flagging systems do today.
Wonder why the whole swagger ui was removed.
Of course we can go in and add it our self but I need some explanation.
Because its not maintained anymore I think
Delivering just the standard OpenApi document allows users to use whatever they want. So if one is more of fan of Postman, Insomnia, Rider, etc. you can just import that spec. The other thing that one should worry about is external dependencies, both "what" and "how many". Every external dependency you have could become a risk. For example it might die, get stale, become monetized, even becoming a security risk.
I am purely guessing, but I think the original solution might not be AOT friendly as they might use Reflection for discovering endpoints, etc.
At this point we really need a reboot of the whole thing where allocations are minimized by default
Hey Nick, if I switch from monthly Dometrain Pro to yearly will I be able to use Black Friday Promo code?
Yes
How is the new Features thing different in practice from reading a bool from IConfiguration?
It completely removes the code. Reduces the evaluation by a single if check. Basically, the same as using a ConditionalAttribute w/ a compilation symbol.
System.Diagnostics.Conditional > FeatureSwitch
Interesting. Can i use IMemoryCache without the HTTP stuff? In normal .NET, like in a console / WPF app ?
Great video! By the way, the hybrid caching was already covered on another video. Thanks for all the amazing content! 🫶
Nick, when a video about Agile/Scrum/SAFe/etc. and how they have become a jail for really productive developers?
Wait a second, how Feature Switching interact with all the previous ways to obtain feature flag? Like App Settings files? Or Feature Management? Azure App Configuration or other external feature flag management systems?
If there a way to configure this feature in runtime (let’s say we are using configuration from aws appconfig)
18:06 "... and kill another library" 😭
Press X to Jason.
Nice choice of variable name for a dictionary 😂
I’m annoyed Microsoft is still using out parameters in new features. Is it so hard to return an option or result type or just use null?
why was helloWorld greyed out after you changed it to readonlyspan?
because it wasn't used, and was offering a refactor to get rid of it.
@@billy65bob but it wasn't before that either
So... "Feature switching" was implemented in COBOL in the 60s and it was WAY simpler to use. Color me unimpressed by this feature. It's also redundant as you've been able to use arguments to do the same thing since probably .NET 1. And if you wanted to drop code out of the compiled output, you could use compiler switches and #ifdef.
I also thought it was a useless feature since you could use appsettings.json or #defines.
FeatureSwitching is a strange mix of compile time and runtime. The project file settings getting built in and the stripping of the code for AOT has compile time behavior while the actual lookup from the static AppContext is runtime.
They use that way to setup parameters for GarbageCollection etc. since NetCore and i kinda like choosing between a json and project file better than the old appConfig.
In a way it is probably more convenient than building your own static config class or #ifdef but it doesn't seem very elegant to me.
Totally random numbers 69 and 420 :)
Does domtrain have some certifications I can add to linked in? XD
Ofc it does
Why should someone use Feature Switch when there is IConfiguration and IOptions? It looks completely redundant right now.
An interesting hybrid cache feature - tags - is not implemented
dict instead of dic for the next one :D
And about the F# news? 👀
Am I smoking something? LOL... In your first example, my first thought (before you changed the code to use the || operator), I thought you ought to default that to false, b/c if the TryGetFeature fails, you wouldn't want to default to TRUE... but anyway... then you converted the statement to use an OR for the result... but isn't the result of one of these TrySomething(...) calls the result of the "try"? Meaning that if it fails, it returns FALSE, and if it succeeds, it returns TRUE, and the actual result (assuming that the try succeeds), is whatever is output (in this case TRUE if the feature is enabled and FALSE if not)... so in that case, since the TRY always succeeds (e.g. TRUE), and you did a || isEnabled, the result of TRUE || anything is always going to be TRUE.
Sorry to be so wordy... Maybe I missed something, because clearly your demo worked, but I'm just wondering what I failed to catch there.
basically if the feature switch can't be found the feature is on by default, otherwise use the configuraiton that was found in the settings. You can change that behavior however you like
Bro had to guess two random numbers, and he guessed "69" and "420", totally random numbers, I don't even know why I am pointing that out.
What's so special about 420?
It’s a drug reference. Personally I think it’s pretty lame.
Press X to JSON
"And kill another library" yep Ms doing Ms things 😅
New guids are not compatible with sql server sorting, beware :) if only two products were made by the same company...
ORMs are only for simple ABMs, procesing through and ORM is asking for trouble. That is what SQL is there for.
I am still disappointed that params IEnumerable (or a collection expression converted to IEnumerable) constructs a hidden ReadOnlyArray behind the scenes from the array. Like why? An array is already an IEnumerable, and since it gets created too at that point, there is no code that cares about the elements after the call. Nothing except the target may observe any modifications, and not having access to the underlying array misses out on optimizations performed by the target.
"params" or collection expressions should construct the strongest implementation of the interface (that being an array in the case of IEnumerable), not the weakest implementation.
Just don't use params or collection expressions with interfaces.
Nothing really new for blazor. They said “Aspire” almost hundred times in this .net conf.
Very disappointing.
No discriminated unions again :(
Unions is still years off at this stage. It has only been proposed. They're still at the preliminary stage at having proposed an API. They still have to incorporate feedback, implement and validate prototypes, etc. But we still have nice packages in the meantime that can help cross the gap, e.g. OneOf.
"dic"
Saw a few comments about why Nick was using 'decimal.Add' over the '+' operator so thought I'd step in to help clear up any confusion.
You would think that these two ways would do exactly the same thing. Well if you read the official C# documentation you would find out that you are completely correct and the reason Nick uses 'decimal.Add' is cos he's a crazy muthafucka.
what a waste
So the AggregateBy and CountBy allow us to do what we could already do easily and more readable as follows?
from lesson in completedLessons
group lesson by lesson.CourseId into g
let totalWatchTime = g.Sum(lesson => lesson.LessonDurationSeconds)
select $"Course id: {g.Key}: {totalWatchTime} seconds";
from lesson in completedLessons
group lesson by lesson.LessonId into g
select $"Lesson id: {g.Key}, {g.Count()} times";
Am I missing something?
Feel like he's a little too old to use sexual innuendos in his videos. What is this .NET for middle schoolers? I don't know if anybody actually finds this funny but it really makes him seem less credible and immature.
'Dic' is not the same as 'dick', a synonym for 'penis'.
24:30 "sequential", really? They are not sequential, did you look at it? That's the issue. You need ULID, not this mess called UUIDv7.
Sortable would have been the right word
@nickchapsas it still doesn't sort right. You can sort a classic GUID, too, if you wish. Its going to be random order. You can see it from your own video that those 3 values would sort into wrong order.
what do u see as the purpose of a GUID?
@@nickchapsas monotonous
@@GigAHerZ64you look at them as if they were strings and try to sort them lexicographically left to right. There's a specific bytes placement logic, some of them are even sorted in reverse. Try to sort 20 GUIDs, you'll see.
Thanks for the overview Nick. I still write C# at work, but personally I never create a new project in C#, when there is Go. Overall .NET 9 is a disappointment to me, there are some nice features, but it seems they are trying to please everyone, and lack a strong vision for the future of C#/dotnet. It's a perfectly capable and productive language, just not for me anymore. Thanks
first view and comment
First!
42069. nice....
youtube.com/@ididathing 😂