Webinar Transcript - What’s New and Exciting in .NET 6 and C# 10

Anne: Welcome to a webinar Wednesday, thank you so much for joining us. We're excited that you're here today and I'm here with my colleague Jason Bell, and he's going to be presenting what's new and exciting and .NET 6 and C# 10. We will be starting in just a moment. I did want to let you know that this session is being recorded. We'll be putting it up on our YouTube channel. It will be at our site at https://www.accelebrate.com/library/videos, and we'll also be sending out the URL to you after the webinar. Just to tell you a little bit about Accelebrate, we've been in business for almost 20 years and we do a lot of training every year all over the U.S., worldwide and of course, now online. We are just starting to do a couple on-sites here and there. However, we are doing mostly all online training and everyone has adjusted really well to it. You'll see Jason's set up when he starts doing the webinar and you'll see he's pretty state of the art. We do teach a lot of .NET classes and Jason teaches them all. We also teach programming languages, data science, Microsoft Power BI, SharePoint, Office 365, also AWS, Security, DevOps, React, Tableau and a lot more.

But today, of course, we're here to talk about ASP.NET Core 6 and Jason does teach this class among many others, and he's also really good at getting them on a phone call, customizing the class for your team of three or more. If you go to https://www.accelebrate.com/asp-net-training, you'll see all of our ASP.NET classes and you'll see the ASP.NET core 6 classes there. So just a little bit about Jason, and I've had the pleasure of working with Jason for about 10 or so years that Accelebrate, No, I think a little more, maybe almost 12 and he's been working with .NET for over 20 years. He is an author of five .NET development books. He's a contributing magazine author, and he's been a technical trainer for 18 years, and most of them have been with Accelebrate. And he's probably taught at least four thousand students for us, and with all of his student evaluations, 99% of his students said they would love to take another class from him. But he does not just teach it, he actually lives and breathes it. Our Accelebrate website is 100% ASP.NET Core 6, and he built the whole thing from scratch, from the ground up. He's not just teaching this stuff, he's actually bringing his real world experience into the classroom. Let me turn this over to Jason so he can start dazzling you with what's new and exciting.

Jason: I do have a handful of slides, but there's no bullets on any of these slides. It's just sort of the main stuff we're going to talk about. You may recognize the first thing here, which is the visual studio icon. We'll talk about that first. As Anne mentioned, I use ASP.NET core 6 now for the production work or public website and some other internal projects. If you're a .NET developer, you've probably seen there are lots of YouTube videos and blog posts and everything that is out there about what's new in .NET 6 and C# 10. I'm not going to just go down the list and just go through each thing because you can easily find that sort of thing online. What I'm going to do instead is just sort of, casually talk about some of the things that sort of are impacting my life as a developer right now. There's a few things that are new that are more, behind the scenes or they're performance improvements. There's a lot of great performance improvements in .NET 6, but you know, it doesn't really impact your day. I mean, your applications run faster, that's great, but I'll talk much more about how changes are actually going to affect the way you write your apps, the way you write your code on a day-to-day basis. Some of the things that maybe are new that I don't really use and I'll talk about why, but other things that I do use. For questions feel free to use the questions feature there in the go to webinar interface and ask questions at any point along the way here, and they'll get queued up in that questions area there. I'll see them and then what I'll do is at the end here, I'll go down through the list and sort of answer as many questions as I can. Hopefully, we can get them all but feel free to go ahead and put them in there as we go along and you can get it in the queue there. Something we're talking about or whatever it happens to be.


The first thing is tooling. We have a new version of Visual Studio, It's Visual Studio 2022. For the first time, it's actually fully 64 bit now, which is nice. You may have heard that it doesn't really have a big effect on most people's projects because honestly, the reason Microsoft waited so long to make Visual Studio 64 bit was because they didn't need it. It just wasn't necessary. The only time you'll really see an effect is when you're dealing with huge projects or having that additional addressable memory is a benefit because there are people out there that have solutions with hundreds and hundreds of projects with millions of lines of code. They can potentially see some benefit there as far as performance and things. For me personally, I do a lot of my development on a Mac. This may not affect you if you just use Windows hardware, but with Apple, with the introduction of the M1 stuff and the arm-based hardware. .NET 6 is also the first version of that to run natively on arm hardware .NET five could run. But using Apple's Rosetta technology, where on .NET 6, it runs natively, which is wonderful. Now there are some things you might bump into.

If any of you do use a Mac to do .NET core development, say using VS code, you might bump into a few little things here and there still kind of as this transition happened. I'll show you one as an example just to maybe save you some time because I had a hard time finding a solution for this, which was I'm building my apps using Core 6, everything's great. And then I go to use a tool. One of the extra tools that's available for .NET is the httprepl tool. So httprepl allows you to sort of exercise your web API or just make raw HTTP requests, things like that. And so it's a cool little thing. I wanted to use it, so I just said httprepl and I got this error, "Fail to load this dynamic library". I thought it was an architecture problem, in other words, I thought the error was related to me running on arm hardware, but then when I actually looked at it, there is a command you can use on the Mac to specify the architecture that you want the executable to run. As I tried that, it didn't work. If we notice, here it says it is an incompatible architecture. It's trying to load this shared net app 5.0.11 dynamic library. So this will get fixed, it may have already gotten fixed, I'm not sure when they update an updated version of the repel tool, but I ended up having to use a command that you don't see very much, but could also be useful for other things, which is, I'll say httprepl again, but this time I'm going to specify a roll-forward Latest-Major. Now, some of you may know that when you're developing a .NET based application, say in ASP Core App and you deploy to a server. It'll use a newer version of the runtime if it's available, but only using the minor version. In other words, if you build for 6.0 and 6.1 is available, your application can automatically roll forward, but it will not automatically roll forward to a newer major version. If you build for .NET 6 and then .NET 7 comes out and you put the 7.0 runtime on your server, your app doesn't just start automatically using that. That's a good thing because you want to test it, make sure it works with the new version and so forth. So here what I'm telling the HTTP repel tool is it's OK to go ahead and roll forward and use the latest major version on my machine, which is the 6.0 runtime, right? But we want this to use the major version, so it will use 6 and then we can get it to use that and it should work, So I had "latest-major" with a dash in it, so we don't want the dash in there. So you httprepl --roll-forward LatestMajor, and it works. OK, here, it says, disconnected simply because I didn't specify a server to make the request to. But I could do that. I could say httprepl, you know, http something and then the "roll forward latest major". All right. If you're using Windows hardware and you're still on an intel machine, it's not going to be a big thing. But you know, it's a mixed environment now because we have more choices now with the ASP.NET Core.

Hot Reload

Next thing I want to mention is sort of the feature that you probably have heard of if you looked at what's new. It's the one that gets the most press, the most excitement, which is hot reload. There was also a little bit of drama around hot reload because initially Microsoft made hot reload and we'll talk about what it is in a second. They made hot reload sort of a central piece of the .NET system itself. In other words, it didn't matter which development tool you were using or what environment it could work everywhere. Before they released .NET 6. They pulled hot reload from being sort of an integral feature of .NET and just made it a feature of Visual Studio. The community got all upset about this because they announced one thing and then they were changing it. And some people say, this is Microsoft just trying to make more money from Visual Studio, because if you want this really cool feature, you have to pay for Visual Studio. Then later, after the community got upset about this, Microsoft did sort of retract and say, OK, sorry, we'll put it back to being a core component, so now it does work everywhere, which is great. So if you're using VS code on a Mac, you get hot reload. If you're Visual Studio on Windows, you get hot reload.

If you haven't seen it, what is it? Why are people so excited? Well, in essence, what it is, is that if you've got an application that's running and you're making requests to it, you can make a change and then see the change immediately, without having to stop and relaunch the application. Typically, if you're debugging in Visual Studio, you'd run your app, you'd look at it. Everything looks good. This is just our basic ASP Core 6 MVC application. If I want to change the text here or something. I would stop the app, change the text, rerun the app. Now the idea with hot reload is we can see the icon up here that looks like a big old fireball. That's why I put that on my slide to do hot reload. So if I were to say, let's move things around here a little bit. And so I have my text over here. And I should be able to make a change to my code and see that change immediately. There is a way so you can have that go both in when you're debugging, but also when you're not debugging.

If you're running the application outside of the Visual Studio debugger, you can still enable hot reload and there's an option that we'll see in a second, but this is really, really great for UI stuff Because for the ability to make a change to my application and say hot reload and be able to see that change immediately, Like we see here, my applications still running. For user interface stuff, this is great because, you know, we can make changes. See those changes, make more changes, see those changes for making changes to code. Some of your logic, your functionality, your database calls, things like that. It's not as useful. It can still work in a lot of circumstances. I typically encourage folks that if you're making changes to sort of business logic and database code, you probably want to stop and relaunch because once you start making changes to a running app, you're kind of doing things a little out of order. Like, for example, this preliminary code ran, which this later code depends on. You now change the preliminary code, but it doesn't rerun, there can be issues there. For user interface stuff, it's wonderful and you even have a couple of options like you can enable hot reload on file save. Instead of clicking the big old fireball button, I can just make a change here, then just save the file, and you can see the change, over here. As I mentioned, it works both in Visual Studio as well as outside of Visual Studio, so we can see that here in a second. There's also some other options you can see here. I can completely restart the application just from using the hot reload option, or there are some other options here you see as well. Like, do you want to enable it when you're debugging? Do you want to enable it when you're not debugging? So pretty neat feature again, especially for user interface type stuff right now outside of Visual Studio, if we're using VS code here. I have a simple application in VS code and typically what I would do is just do.NET run. I'll launch my application in the traditional way and then when I want to make a change, I restart it or do whatever I like, but now I could also do.NET watch. Now .NET watch has been around for a while. It is a command that has been available in the command line. But now when I typed.NET watch, you notice it says hot reload enabled comes up in the console right here, And it says for a list of supported edits. In other words, you can't edit everything, but you can do a lot of things. But it's now enabled and it's watching my files. If I move some stuff around here again, we can see it should work exactly the same way, which is running here or making our request. If I come in here and I'll make a change to this again, we'll save the file, it made the change immediately. It's been a highly anticipated feature, it doesn't work everywhere.

For example, I was doing a class last week and somebody asked me, "Hey, does this hot reload stuff work when you are using Docker? So when you're running your ASP.Core app in a Docker container from Visual Studio, does hot reload work where you actually like Reload, the application that's running over and that Docker container?" And my response was, "I don't know, let's try it." Right now it does not. Will it later? I don't know. I haven't checked to see what the latest word on that is, but there are some environments or some situations where hot reload just is not going to be able to do it. For its primary use case, which is editing CSS, editing HTML, things like that, it's a big productivity booster, right? Just to save a lot of time. Put your visual studio on one monitor, put your browser on the other, just add files, and it's just changing over there. That's basically "hot reload" right now.

Nulls and Nullability

Next thing I want to talk about is nulls and nullability. For a long time, it's been the case that in .NET, the most common exception that people's applications would experience right things that would crash their app and throw an exception would be the good old null reference exceptions. If I run the application here, if you've done any .NET development before, I have my privacy page setup here to access something that is null. So we get our good old "null reference exceptions, object reference not set to an instance of an object". This is like the number one by far most common exception that you'll experience in .NET, ever since. Net 1. The reason for this is, of course, when we go to access a reference variable and that reference variable is null, this is the result. Now, this is not the case with value types. For example, If I have a value type, an integer or something. Value types by default are not allowed to be null. If you have a value type like an integer or float or a structure, it can't be null. If I try to set it to null, I will get a compiler error that says "this thing cannot be null because it's a value type." There is some inherent safety that comes with that because I know that if I'm working with value types, I will never get a null reference exception because the thing can never be null. That makes my code a little bit more resilient or a bit safer. It's easier to work with. There's a lot of functional programming languages like F Sharp that take this philosophy with everything, which is by default, nothing can be null because it's dangerous, and it leads to runtime exceptions rather than compiler errors. Now, as many of you may know, value types can be null if you need them to be by turning them into something different, which is a null level value type. Now "I" has the ability to be null, but you are explicitly saying "I want this capability. I understand this introduces some danger here." Now I know that I need to write my code a little bit more carefully. I need to check to see if "I" is null or not. I need it this way because look, this value comes from, say, my database where that thing could be null." But reference types are a different thing.

Reference types, by default, can be null. If I have a person object, which is null and I go to access it, I don't get a compiler error, I don't get a warning, it just blows it up at runtime. That's what's happening in my privacy action right now. I have a person which is null and I'm trying to access the person's first name that throws the exception. Microsoft decided that why don't we try to transition C# so that reference types behave like value types do. In that reference types are not allowed to be null by default, either. If I just create a personal object here that cannot be null unless you explicitly ask for it to be able to be null. Nullable reference types, they're trying to bring the same safety to reference types. So it works for all the types. The problem with this is that it would break a lot of people's existing code if Microsoft just all of a sudden said, "Hey, you know, this is the way it is now." Even though the feature, this Nullable reference type was introduced in C# eight, you had to opt into it. You had to ask for that behavior. It was not turned on by default. C# nine .NET 5. The situation was still the same. It was available. You could opt in if you wanted to, but it was not turned on by default. Now in C# 6, whenever you create a new project, the .NET 6 templates do include in your CS project file, this "nullable enabled by default."

Now nullable reference types are enabled by default via this parameter. If you just upgrade your existing project from, say, .NET three or .NET five to .NET 6. You don't get this. You'd have to add this in here, but any new projects you created this feature will be here. We're slowly trying to make that transition right now. For example, if I look at my person type right now because I have that enabled in the project file, I get warnings underneath these properties. It's like, "Well, these look fine. Why are there warnings here?" Well, because it says non-Nullable property first name must contain a non-null value when exiting the constructor, which basically says it's possible right now to create a person object, not give any values for these, which means they'll be null, and because we have nullable reference types enabled, that's a warning. Now to fix this, I could say I want these to be nullable strings so that they have the ability to be null. Now I have to understand that I have to be careful, write the check to make sure these things are not null. Another option here, is I could add a constructor that initializes these to say empty or I can actually put the initializer here, and that will also eliminate the warning. There is a way to put a directive in this file that will disable this warning for this particular file. Probably not a good idea, but if you're transitioning old code, some of those things may be necessary and show another example of that in just one second. With that enabled, we'll get these compiler warnings whenever we're doing something that's not sort of inline with the way you should be using nullable reference types, from now on. I tell people that I highly encourage them to leave it enabled, let it generate these warnings and then address the warnings sort of as you're able right, if you have an old project with lots of code and then you want to bring that forward to .NET 6 and then you turn on nullable reference types. You could get thousands of warnings. You can go through and try to make the compiler happy, which will be a benefit because you'll really make sure that you're never going to get a null reference type or null reference exception, or you could decide, "Well, let's not enable this for our existing projects, but for any new projects that we build, we will." Another question that I will get sometimes is, "well, if I'm really all in on this, I would like it to generate warnings instead of generate errors instead of warnings." That's something Microsoft may do down the road. They may decide that from now on having a reference type and setting it to null without it being nullable will generate an error, a compiler error. If you want that behavior now, you can do that. What you'll do is you'll do this "warnings as errors" as a setting, or nullable types. Now what'll happen is when you build your project. Anything that it would typically flag as a warning for you, because you have null reference types enabled, will now get flagged as an error. You can see here these actions show up as errors now. It must contain a null value. Here's a dereference of a possible nullable type. All these become errors now, and it will not allow me to compile them versus the way I had it, which is that they were just compiler warnings. This is a big, big change.

And even though we're talking about what's new in .NET 6, this is not technically a new feature in .NET 6, it was introduced a couple of versions ago, but it is new that it's now enabled in the templates and that could surprise some folks. They say "file, new project and like what's going on here? Why is this?" Because if they haven't heard about nullable reference types, the fact this is now enabled by default can be a shocker. Now, one thing that you might bump into is you're writing code that you're used to for the past many years and this creates a little issue. You start getting warnings, for example let's say we're using EF core to talk to our database. I said, "Oh, I know how to do that. I create this repository thing and I have it inherit from DB context and I'll create a DB set for each one of my entity types." You do that and get warnings well, because the DB set says this is a reference type, but it cannot be null, by default, but the way you have it defined here, when this repository object is created, this thing will be null because then entity framework is going to set this thing but it comes later, after the object is actually constructed. The compiler says this thing is going to be null for a second. If you try to access that, it's going to throw an exception. I bumped into this and I'm like, "Well, what am I supposed to do here? I mean, I could say this is a DB set nullable that makes the compiler warning go away." but now every time I go to access this thing, I'm going to get warnings that you're not checking if this thing is null because those are the additional warnings we get.

If I'm trying to access something that might be null, but I'm not checking to see if it is null, I get a warning to what's not ideal. What am I supposed to do? I just did some searching and came across this page and the documentation says working with nullable reference types with entity framework and it gives you a few options here of how you do your DB context and DB set to be in line with this new thing. A couple of options were, one is you can just initialize this to a new DB set. Basically initialize it to like an empty one, and it'll say, "Well, OK, I'll just initialize it to an empty one and then the entity framework will then set it to a like a real one, later" makes the compiler happy. Now some people say, Well, isn't that sort of a performance thing because you're kind of creating this empty object, which is just going to get overwritten? Yeah. Not a big deal, though, it's just, you know, a single reference. In the documentation, they say, well, you can just disable the warnings in your DB context file. Later on, down the road, if those things end up being surfaced as errors, there's an issue there. Typically this is the way it is, though, with using these nullable reference types because there's a lot of code that's been around for decades that was written assuming certain things and now things are different. You might have to look and say, "Well, what is Microsoft recommending here? What do they want me to do when I'm defining my DB sets", for example. Then you'll encounter the same thing in your code if you're migrating, your code forwarded to.NET 6. You could look at it and go, "Oh, I get a warning here. Should this be able to be null and then I've got to be a little bit more defensive when I'm using it? Or is this thing really not supposed to ever be null? I'll initialize it to say empty string or whatever it is." And so like I mentioned, this is a very fundamental change in the way we think about C# and our reference types and our objects in our applications. We've sort of been setting up this transition over the past few versions. Now it's time to really start, you know, embracing this and doing this. At the end of the day, you'll be happy because less null reference exceptions, less runtime crashes, the more compiler errors. And that's always a good thing.

A few more features here with the C# language. The next one I want to mention is using. This little graphic here represents interconnections between different libraries and stuff. Using statements, if you've been doing C# for a while, you're very used to adding these using statements, because you're trying to use some type that's in a different namespace, you need a using and if you forget the using, you know you get a little error from the tooling that says, I don't know what the DB context is. You click the little pop up and it says "you need a using for entity framework core", you add the using and you just get used to doing that all the time. The issue is that if you've got a particular namespace, let's say hot reload demo models here, well, there's a lot of code that's using your models all over the place, you may have that using directive in lots of files or things like using system. You got it everywhere. In C# 10, we now have what's called global using and so what we can do is in one of our files, it doesn't matter where we can say something like Global. It's a small thing, but it can make a big difference because now this using will take effect across all the files in my entire application. If I get it here, I don't need to ever put it anywhere else because it's going to be the same effect as me putting this using statement at the top of every single file in my project. Sometimes now what you'll see in projects in .NET 6 is we'll have a C# file all by itself called globals or something. Then in there you'll just have a bunch of global usings and you can put things in there like your models namespace or your data access namespace. Now, when you're adding a bunch of usings, of course you always want to be aware of things like collisions. But you can deal with those when they come up. You might have a using where you can't do it as global because it collides in some other place in the project. If you do it unnecessarily, so let's say I define this global using here and then you kind of forget or you don't realize you're doing that. And then over here I have using any framework core, well, the tool will gray it out. If I hover over it, it says using directive is unnecessary. You don't need this here. You can put it here if you want, but you don't need it here. And so you can just remove the unnecessary using right now, along with global usings as we have something Microsoft calls implicit using. Implicit using simply means that you are referencing some package, some library that has some global uses.

Would you like to pull those global users into your project? If we look at our CS project file again, all of the .NET 6 templates will have this implicit using enable here by default, and you can remove it if you want, but it's going to be there by default when you create a new project. Again, if you're upgrading from an older version of .NET you might want to add this potentially. What this will do is pull into your project the global uses that are defined by the ASP.NET libraries. There's 11 of them. If it's an ASP.NET Core project, you'll bring in 11 of these global usings, and so they include the basic stuff like system, the new text, JSON system, and there's a list in the documentation. There's 11 of them in total. Normally, that's fine, but if you really don't want that, you don't have to use that. But it's just making it clean. It's cleaning up what's the minimum amount of code we need in our files to make it work? Since we're pulling in these implicit usings, then a lot of our files that used to have five or 6 usings at the top don't have any. They're still there, It's just they're being pulled in from somewhere else. It's a small thing, but it really affects the way I write my code because I know a lot less using statements. Sort of along with that, is something called file scoped namespaces. This is another really small thing, but it's probably one of my favorite features and C# 10 and .NET 6, which is if I look at a type here like, so this is the person class, and it's in this namespace model. What this means is now this entire person class has to be like indented over right because it's inside the block for the namespace. That is a really big class. You always have to have it sort of indented over because it's in the namespace. When I'm doing introductory classes for people that are new to C#, one of the most common mistakes they make is when they get down to the bottom of their file. They just don't have the right number of closing brackets because there's namespace function, there's the class and it's like how many closing brackets do I need. The other thing about C# is that you can have multiple types in a single file. You can even have multiple types that are in different names, spaces in a single file. It's completely legal for me to do something like this. This is a different namespace. It's a different type. I have one file with a class and one namespace and a different class and a different namespace, all in one file. This is fine. But most people don't do this right. Most people typically do one class per file, the class has the same name as the file, and it's in some namespace. That's the way, like the vast majority of C# code that is written. Microsoft added this nice little convenience, which is instead of having to have these curly brackets and indent everything over. We can just take the namespace through a semicolon on the end of it. Get rid of the block. Then we can move our person class over. So the stuff that's in this file is in this namespace. Here's a type. I love this. Now what you'll notice is that the templates in .NET 6. So for example, if I come over here and I say, add a new class and I add this class to my project, it will not use a file scope namespace. It's going to use the old style way with the brackets, because that still works. So you'll have to like, change it or create your own template or something. This will always work. The first thing I do now when I create new classes and things and my .NET 6 projects is I just switch it over to be a file scope namespace because I just like it. It's clean. It's nice.

Along this idea of making it less code that's necessary for you to write is when it comes to our main method. Every .NET application has to have a main method, whether it's a console app or a web app. It doesn't matter. It's got to have an entry point, which is defined by main. First code that executes in the case of our ASP.NET core web app. The main method is what sets up our host and registers our services and our middleware and all these different things. If I was to create a new project, In Visual Studio. And some of you probably are very familiar with this. And what I'll do is I'll say "file" on the project will go with a new MVC application and we'll call it just a web application. One will choose .NET five. We will create this. What we'll see is we have a program CS and a startup CS. This is about sort of ASP .NET application startup and Program CS contains our main method. There's the good old main and then main. Just call some other function and then this function uses some startup class and inside our startup class. This is where we have two separate methods: configure services and configure one that sets up all our services, one that sets up all our middleware. This works well. There's nothing really wrong with this.

When Microsoft looked at this, they said, if we compare our sort of initialization or setup to other frameworks that are out there, like Python or go things like that, our platform looks much more complex because when you're looking at using something like go or Python, it's like you can write a web service with three or four lines of code or if you look at your typical Hello World application, it's like two or three lines of code. If we look at the C# equivalent, there's this using statement and this namespace and then program class, public class program because all code has to be in a class. Then this main function and then inside the main function, this is where you'd have, console right down. Hello, world, What Microsoft did, one of the features they introduced was this idea of what they call top level statements. You're still going to have a main method because you need to have it and the code still needs to be inside of a class because it needs to be OK, but it can generate a lot of stuff for you. If I create a console app using a .NET 6 and I look at my program CS, this is our hello world and this is it. The typical Hello World application .NET 6 can now be one line of code, console right on Hello World. Before we had to have the using, we don't need that now because we have globally implicit using. It's pulling in global using systems. Namespace, well, we don't need that because technically we don't need a namespace because it'll put any code that's in here in the root namespace, which is OK for like our main method basically. Doesn't have a class program. We'll come back to that in a second. And it doesn't have the function main.

What the compiler does in .NET 6 is if it sees that you have statements, executable code statements that are outside of any sort of type, and they call this a top level statement. You've got top level statements like this, it's basically going to say, "Oh, this must be your main method." It's still going to create the class. It's still going to create the main method and then it'll put this code into the main method. We're to look at the IL's generated from this. We would still have the class, we would still have the method. All that stuff would still be there. And then this code would just be inside the main method. From the programmer's point of view, we can now write a fully functional .NET application with one line of code because it's a combination of the implicit using feature that we just mentioned and this feature of top level statements, which means now if we go back to our ASP.NET application where we had the one, where we've got the start up and the Program CS and all of this, we can simplify and clean things up a bit. If we take a look at our .NET 6 web application, where now we do not have a startup CS anymore. If I look at Program CS, I don't have my main method. Now I have a using up here, but I can probably get rid of that because I think I made that global. And so all of this code is my main method.

There are a few other helper classes that have been added as well that we won't have time to get into all of it here, but the web application object, just a wrapper around some of the stuff that we did more manually in the past. And so we can create that and then we still configure all of our services. This is all the code that would be in configure services, typically. Build our application, configure our pipeline. This would be all the code that would typically be in the configure method. All of our middleware and things. Then at the end we run. Same as before. It's just been that now we have our application initialization as just this one file of executable code. But we know that, yes, this is going to get wrapped inside of my main method for me when it builds but I'm still doing the same stuff. I'm still setting up my services and setting up my middleware. It's just all here now. Some people prefer to still have a helper start class so they can put the code in there and call it from here. That's completely fine. Other people say, Well, I just like having it all just in line right here. It's easier to follow, it's more straightforward.

One nice little handy feature about web applications, is if you're an ASP.NET developer and you've been doing it for a while, one thing that I ran into that you may have run into, is that when you're config setting up your services and middleware, sometimes it's difficult to get your configuration information. The stuff that comes from your app settings. For example, when you're setting up your middleware, you have access to your config. When you're setting up your services, you don't. Because they say, "well, that part hasn't been done yet." After configure services runs then all the services will be available for dependency injection, which includes the configuration. That's why it's available here. But in here, it's not easy to access your configuration. Well, with the new stuff. One of the things they made with these little helper wrapper classes, is that when this guy gets initialized, it reads the configuration, so it's available to you immediately. When you're setting up your services, you have access to all of that. You can do something like "builder.configuration'' and that gives you access to all your configuration. That came from app settings, command line arguments, environment variables, all of that stuff. Then when you call a build, that will construct your D.I. container, all your services and everything, and then you can go on to do the rest that you need to do, like set up your pipeline and all of this. This is the one, and none of this is a breaking change. You're taking an older version of your apps, one that targets .NET 5 or even .NET Core 3.1 and you move it to .NET 6. The old way still works perfectly fine. You still have your startup class, main method. If you create a new project and you see this format, you're like, "Whoa, this is completely different. What's going on here?" Well, we're just combining these new features. We're using top level statements, implicit usings and putting everything in one file, combining these features together. Then as we write our code, we realize that oh, while there's nullable reference types now and that's enabled by default, so I've got to define my types a little differently or write my code a little differently to accommodate that. There really are no breaking changes that I'm aware of other than maybe some extreme edge case stuff going from 3.1 to 5 to 6. We just get some new features, some new options and different ways of doing things. Some ways to improve our code safety by using nullable reference types. But all your existing code from 3.1 should pretty much work, as is all the way forward to 6, but it can be a good idea to sort of try to start adopting some of these new patterns and these new features, because in some cases it just makes your code less of it. But in other cases, nullable reference types, it can really improve sort of the safety and we get less runtime exceptions and those sorts of things.

Record Structs and Record Types

Like I mentioned, there are more features that have been introduced. If we look at this list here. Record structs or record types. This is the new thing as well in C# 10. This is one of those features, that you can look at it and read about it, but it's interesting. I see why they're introducing it, and I can see some places in my code where it could be useful to me. However, I'm kind of on the fence of like, "am I really going to work on adopting this now? How much of a benefit does it give me? Not sure." This is not really affecting my code today yet. We've got a lot of behind-the-scenes improvements, global using as we mentioned. File scope namespaces, we mentioned improvements on lambdas again, just making them better, performing a little bit easier to write more behind the scenes performance type stuff.


With that, I think we're going to go ahead and kind of wrap up here because those are the main things I wanted to sort of mention is some of the new tooling stuff Hot Reload, Nullable Reference Types, which we could talk for a long time about nullable reference types and all the different scenarios and things but that's a big one. Global and implicit using top level statements and just how those features all come together to give us a sort of cleaner, more compact initialization for our ASP.NET core apps. Those are some of the big kinds of exciting things that may encourage you to sort of move forward to .NET 6 from what you're using today. A lot of really good behind the scenes, performance improvements and things like that. That's just as you can read about and so if you're trying to convince the boss or whatever that you should move to .NET 6, some of those performance improvements, even though they don't really change how you write your code, can be a huge benefit. Less cost and hosting because we can use smaller instances.


With that I'm going to check the questions here and we'll see if we have any questions and again, feel free to sort of pop them into the questions panel there. The first question here was, "can you give us your thoughts on the use of VAR versus explicitly specifying type?" This is a very popular question. VAR has been around for a long time, the idea with VAR is, we don't have to explicitly specify the type when we declare a variable right, we let the compiler figure it out. The first quick thing on VAR here is that functionally it is identical to explicitly specifying the type. Using VAR here or using person here. All of the code that comes after this will work exactly the same in terms of performance and also in terms of compiler checking IntelliSense. All of that, it's going to be the same. By using VAR, I cannot, for example, change this into something different later on, I'll get the same error right, that you cannot convert to a different type. I cannot implicitly convert string to person and all of this. As far as performance and type checking and all those things, it comes down to just programmer preference. This compiles the same thing by the way, when you compile a Sundial code is VAR, it's turned into a person. The output you get is the same in both cases. When would I not use it, though? Well, I tend not to like using VAR if it hurts readability of the code. So I say something like VAR P=GetThing. It's not immediately clear to me the type for "P" here. I have to go look at the get thing function and see what it returns to figure out what the type of "P" is. The compiler knows because the compiler can do that very easily. But for me, this code now is not as readable and clear to other developers, so I might want to put the explicit type there for that reason. Just for readability reasons. Another one is that implicit typing has certain defaults. For example, if I say VAR n=6.5, what is the type of "n" here? Is it a float? Is it a double? Is it a decimal? Well, the implicit typing rules have certain things built in, so in this case "n" will be a double. That's just what it defaults to, again, unless other developers know that it sort of hurts the readability here versus me putting the explicit type in there. Bottom line user preference, as long as it doesn't hurt the readability of the code for other people to understand or for you to understand three months later. I typically will use VAR. You know, a lot. Most of the time I use VAR, but sometimes I'll use VAR and then I'll go "I think I'll put the explicit type in here just because it's readability suffers".

Another question that came up, "What features would you like to see in C# that aren't here yet?" One of them it's always bothered me since the very beginning of C# is that it's not easy to know what exceptions a particular method may throw. Other languages like Java, for example, have a throws attribute so you can annotate sort of that. This method could throw these exceptions. By the caller that you should be catching those because they might get thrown right and C# just doesn't have that. If I call something like sequel connection open, I have to go check the documentation to see how could this thing throw an exception or look in the little IntelliSense pop up to see? Does this potentially throw exceptions, are things I need to catch here. I don't get any compiler help where the compiler would say, "Hey, you realize this thing might throw an exception and you're not catching it or looking for it." You might want to do something about that as a warning or something. That would be nice. There are reasons that that's difficult, and there are reasons Microsoft has chosen not to do that, but if someday they were able to figure that out. I think that would be pretty neat and helpful to your code just to turn more things into compilers. I'll throw out another one here because I don't see any other questions. It would be nice to have a little bit more compiler help for some of the artifacts that we use in our ASP.NET web applications.

For example, routing right when we're setting up our routing in our ASP.NET Core apps. We specify that to get to this particular method, you need to do a get four slash privacy. If you have conflicts with your routes or there are certain routes that are ambiguous or unreachable. You won't find out until you run and test the application and you get runtime errors and things. I understand why the compiler doesn't check these because these are things inside strings. They're attributes, and it would be very expensive for the compiler to always be checking that stuff, but a little bit of some sort of different, improved design time support for knowing when I have issues in those areas would be nice. Same with when we're writing our views because in our view, files were mixing C# with HTML. I get some help here, but not as much help as when I'm just writing pure C#. I know this is also a very hard problem for Microsoft to solve, because this is some complex stuff going on here with the Razer parser, but it just would be nice to eventually, you know, see some more improvements in that area for some more design type help.

Another question that popped up here. "Do you have any suggestions for the best approach for upgrading existing .NET framework applications to .NET 6?"  First, I will say that for different versions of .NET Core and again with naming Microsoft has sort of dropped the word core for, five and 6, even though five and 6 is really a continuation of Core. When we're talking about the ASP.NET, we still use the word core just to kind of avoid confusion. But if you're upgrading on that track. So let's say a core 3 app .NET 5 or a 5 app to 6 or core 3 to 6 or even core 2 to 6, that's going to be pretty painless. And there are some good documents online about sort of doing that. If you just say, migrate, I know you asked about that in the framework, I'll get to that in a second. But Microsoft puts out these migration guides. So for moving from 5 to 6, for example, changes that you have to make and then changes that you may want to make, but you don't have to make, like enabling nullable reference types and things like that. Now, when we're switching tracks, so we're going from .NET framework to .NET core or 5 or 6, that's going to be a bigger transition. If you've done a good job of architecture with your .NET Framework app, so you have your business objects and your data access code and all that separated from all of your user interface stuff, your web service stuff, your web application. All of your business objects and logic should come over as is, so you should be able to bring that code into your .NET 6 project without really making any changes to it. Because the types are the same, the API surface area is pretty much all the same. Then when it comes to the application, the ASP.NET web app, so you're going from web forms to core, Well, web forms are not in core. That's going to be a big piece of work there to sort of transition that stuff. But hopefully all your underlying stuff can come over your lower tiers and your stack and kind of come over without changing, but generally to make that transition. I would go into Visual Studio, say, "file new project .NET 6" and then just bring the code piece by. He's into the new one. Maybe lots at once, maybe a little bit at once, but do that rather than just trying to, you know, upgrade it from framework four point eight to .NET6 like in place. Create a new project and then bring this stuff in as you can. If you can bring whole, entire assemblies in, that's great. It depends on your project. It could be a lot of work depending on how things are separated, things like that.

On this topic, real quick. I had a group last couple of weeks ago that had WCF services and they said, How do we migrate from WCF services to Web API, using .NET 6? What I said to them was, well, you can leave your WCF service as is because WCF is not in .NET 6 and it will not be they're not planning to bring it forward. Create your new web API, but just have your new web API, call your existing WCF service. It's an extra network hop, but it can be easy to sort of do that. And then you can start migrating individual API endpoints to not have to use the underlying WCF service anymore, right? You got one hundred and fifty endpoints, ten of them now just talk to the database directly where the other ones still go through the old WCF service until I can migrate all of that functionality and then I can drop off the WCF service. So you're paying the cost of having that extra hop just as a mechanism to sort of transition slowly. It kind of depends on the app and so forth.

I've kind of reached our end point, but I think there was one other question that popped up. Yeah, clarity about the VAR thing, some developers I've come across are like, I'm never going to use VAR, I'm explicitly typing all my stuff. I have no problem with those people. That's completely fine. There are some places, though, in .NET where you're forced to use VAR specifically with anonymous types. Let's say you have a web app or an example here because time and say we have a web app and we're using data projection with a link to retrieve product objects, but then turn them into some different object to then serialize as JSON. And we're using an anonymous type. The VAR thing=new, and I'm just creating some new type here out of nothing.  Well, this object doesn't have a type, it's an anonymous type. I have to use VAR here and then I can serialize this thing in the JSON and return it. Even for folks that say, I'm never going to use it. Well, there probably are going to be places where you end up having to use it. You just use it when you have to. It is kind of a preference thing. I'm like, do we like our curly brackets on a different line or same line? And some people say, well, different lines are more readable. Other people say, well, the same lines. More compact, you decide. But you'll find out, You're like, I'm never going to use VAR and then you reach one of these points. Well, you'll know about it because you'll be like, What am I supposed to do here, and you find out. I don't see any other questions. With that, I think we'll wrap up here. I'm really excited about .NET 6, I think it's wonderful. It's a great release. As I mentioned earlier, we do have a few courses on it now and if you want more on it.

Learn faster

Our live, instructor-led lectures are far more effective than pre-recorded classes

Satisfaction guarantee

If your team is not 100% satisfied with your training, we do what's necessary to make it right

Learn online from anywhere

Whether you are at home or in the office, we make learning interactive and engaging

Multiple Payment Options

We accept check, ACH/EFT, major credit cards, and most purchase orders

Recent Training Locations












Little Rock


Los Angeles


Orange County


San Diego

San Francisco

San Jose



Colorado Springs







Fort Lauderdale


















Cedar Rapids

Des Moines







New Orleans













Ann Arbor


Grand Rapids



Saint Paul




Kansas City

St. Louis





Las Vegas


New Jersey


New Mexico


New York



New York City

White Plains

North Carolina












Oklahoma City







Rhode Island


South Carolina











El Paso


San Antonio


Salt Lake City









West Virginia








British Columbia




Nova Scotia







Puerto Rico

San Juan