In this episode, we’ll go through the main changes required to upgrade our currently ASP.NET Core 2.2 applications to ASP.NET Core 3.0.
For the walk-through you can check out the next video, but if you prefer reading, skip to the written synthesis.
The playlist for the whole series is here.
I guess it’s time for a boring episode, as .NET Core 3.0 came out recently, we need to get our projects updated to the latest version.
There aren’t too many required changes, but being a new major version, there are a couple of breaking changes and some new recommended practices.
I probably won’t go through every change in this post, so if you want to check out all the details, take a look at the diff in the GitHub repositories.
Basics and other quick stuff
Let’s begin with the simplest bits.
The most obvious is of course going through all the
csproj files and bumping up the .NET Core version, by replacing the target framework, currently
The second most obvious change is upgrading the NuGet packages, namely the ones from Microsoft that have their versions tied to the framework version, but even others (e.g. IdentityServer4) sometimes keep their versions paired with the framework’s. In this case, for simplicity, I just used the IDE and updated all the packages to their latest stable version.
Other quick changes were:
SetCompatibilityVersionmethod, which we called when setting up MVC is no longer needed, at least with 3.0, maybe it’ll be back in 3.1.
GroupEntityConfiguration, when setting the auto increment column,
UseNpgsqlIdentityAlwaysColumnis deprecated in favor of
IHostingEnvironment, used for instance in the
Startupclass is deprecated, now replaced by
IHostEnvironment. If specific web applications bits are needed, the alternative is
IWebHostEnvironment(which inherits from
- We can remove the reference to the metapackage
Microsoft.AspNetCore.App, because it’s implicit when the project SDK is
Microsoft.NET.Sdk.Web, as is the case. If we weren’t using this SDK, we should move this reference to a
FrameworkReferenceinstead of a
PackageReference. For more info, go here.
- Entity Framework Core 3.0 now targets .NET Standard 2.1 instead of 2.0 (though this will be reverted in 3.1).
Packages extracted from the ASP.NET Core shared framework
With this release, some packages that were part of ASP.NET Core shared framework (the things we got when referencing
Microsoft.AspNetCore.App), so we need to reference the ones we’re using.
The main package in this situation is Entity Framework Core. The main reasons for this is change seem to be to make the process of targeting EF Core the same whether it’s an ASP.NET Core app or not, as well as not forcing the upgrade of EF Core version when upgrading the framework. More info in the migration docs here.
Other extracted packages that we must now explicitly reference are
IWebHost → IHost
Previously we were using
IWebHost (along with
IWebHostBuilder) to setup the host of our web applications. When we wanted to build other kinds of services, for instance a message queue consumer, we would use the more generic
IHost and its accompanying
IHostBuilder (we’ll eventually implement such services in this series).
With ASP.NET Core 3.0, everything was consolidated under a single interface, the generic
The main class which looked like the following:
Has been adapted to:
Endpoint routing isn’t something new in ASP.NET Core 3.0, being introduced in 2.2, but it gains more visibility with this latest release.
The goal of endpoint routing is to make routing usable in ASP.NET Core in general, not just be a feature of MVC.
As an example of the changes this introduces, let’s see the
Configure method of our group management API
The new bits are the calls to
UseRoutingmarks the point in the pipeline in which the route will be resolved, so any middleware that comes after it knows what endpoint will eventually run.
UseEndpointsis where we configure the “routable” components, in this specific case we’re configuring our controllers (more on why no MVC later). If we take a look at the options autocomplete gives us when we dot on
endpoints, we’ll see things like
MapHub(for SignalR) or
MapBlazorHub(for Blazor), which are taking advantage of the new routing capabilities.
We’ll certainly make more use of these new routing features in the future, but for now we’ll stick to adapting our existing code to the new recommendations -
UseMvc still works, but we might as well start using the new bits.
One thing that we can immediately see, that’s taking advantage of this new feature is the call to
UseEndpoints. Previously we would enforce that the controllers required authorization through an MVC configuration, as we can see below.
While this still works, so we could have left it, now having routing information available outside of MVC allows to do some things in a more generic way. In this case we’re not getting any particular advantage out of it, but if we wanted to use the same authorization infrastructure we’re used to in MVC with other kinds of endpoints, we could.
RequireAuthorization has some overloads to further configure the authorization requirements of the target endpoint, but we don’t really need it here as we can just setup a default policy for the group management API. This can be done when calling
Segregation of MVC components
From the previous section you might have already noticed that MVC components are now better segregated when configuring the application in the
Startup class, as we can see in both
ConfigureServices side, we can still call
AddMvc to get everything setup, but we have some new options if we don’t want to use everything:
Similarly, on the
Configure side, inside
UseEndpoints we can map things individually, by calling methods such as
Now we can use what we need in our applications. In the group management API and the BFF we only need the controller bits, in the auth service we’ll use everything.
Breaking change in route resolution of async actions
Quick but important shout-out to a breaking change in route resolution of async methods, is that the
Async suffix is removed from the action names.
As an example, the group management API’s
GroupsController.AddAsync action returns a 201 with a link to the
GetByIdAsync route. Previously we would do this with:
With ASP.NET Core 3.0, we need to get rid of the
Async suffix, like so:
That should be it for our upgrade to .NET and ASP.NET Core 3.0. There are a lot more things going on, so be sure to check out the docs, we just went through the main things that affected what we developed so far in the series.
For the full diff be sure to look at the repositories on GitHub, and feel free to ask any question. I took the opportunity to do some unrelated changes, as I was touching some code I wanted to improve. but most of the diff should be upgrade related.
Links in the post:
- Migrate from ASP.NET Core 2.2 to 3.0
- .NET Generic Host
- Endpoint Routing in 2.2
- Comparing Startup.cs between the ASP.NET Core 3.0 templates
- Understanding ASP.NET Core Endpoint Routing
The source code for this post is spread across the repositories in the “Coding Militia: ASP.NET Core - From 0 to overkill” organization, tagged as
Sharing and feedback always appreciated!
Thanks for stopping by, cyaz!