It’s been over 5 years since I started a greenfield project that initially was developed using Katana/Owin with ASP.NET Web API. Over the past 3 years, since .NET Core 2.0 was released and .NET Standard 2.0 was defined, I’ve been slowly chipping away at migrating to .NET Core. It’s been a long road but we’re now fully migrated and running in production with ASP.NET Core on .NET Core 3.1.
Migrating from .NET Framework to .NET Core
This blog post is in a series about migrating from .NET Framework to .NET Core.
To give some sense of scale, our application consist of two primary entry points.
The first is an HTTP API that started out as a shelf hosted (not using IIS) Owin/Kata using ASP.NET WebAPI. Over time that morphed into using Nancy. During the migration, it ultimately moved to ASP.NET Core using the OWIN Middleware to still use Nancy.
The second entry point is a worker process that processes messages from a message broker. This is primarily for handling events and fire-and-forget commands. This has always been a console app.
To give a sense of scale, I ran NDepend over our solution to get lines of code metric, just to give some idea of the size.
Journey: Migrating to .NET Core
I’ve given a full experience report on my YouTube channel. This covers pretty much everything I’ve done along with the process of getting our app running under .NET Core in production.
Questions or Feedback
If you’re thinking of doing a migration and have any questions, let me know in the YouTube comments, in the comments on this blog or on Twitter. I’d be glad to help if possible.
If you’ve done a migration and would like to share some tips, get in touch with me!
If you have a solution with many projects, it can be a challenge to make sure each NuGet Package has the version across all of your projects. Each PackageReference across all csproj files must specify the same version of the NuGet Package. Here are a couple of ways for managing NuGet PackageRerence Versions across your entire solution.
Check out my YouTube channel where I created a video that accompanies this blog post.
First, in order for this to work, you must be using the new SDK style csproj files. This is where your NuGet packages are referenced via the <PackageReference> element.
Here’s an example of the eShopOnWeb.Infrastructure.csproj
In this solution, I also have another project (eShopOnWeb.Web.csproj) that is also referencing the same version of Microsoft.EntityFrameworkCore.SqlServer package.
These PackageReferences must always have the same version matching otherwise you’re going to run into issues.
Instead of making sure each csproj has the same version of a package, you can centralize this by using a file called Directory.Build.targets.
Start off by creating a file called Directory.Build.targets at the same level as your solution file (.sln). This file will contain the PackageReference along with the version. Here’s an example.
This looks very similar to a csproj and how PackageReference’s are used. However there’s one important distinction. Instead of Include attribute, it’s instead Update.
Now back to our csproj, we can now remove the version from the PackageReference
Solution Wide PackageReferences
Another cool use for Directory.Build.targets is to have PackageReferences that are included for every project in your solution. To do this, simply add a PackageReference just as you normally would in a csproj including the version.
In the example below, I’m adding Microsoft.CodeAnalysis to every project.
Note that for this package, we’re using the Include attribute.
The second way of this is using the new (as of .NET Core SDK 3.1.300) Directory.Packages.props.
In order to migrate your application from .NET Framework to .NET Core, one part of the migration is making sure your existing code that targets the .NET Framework BCL (Base Class Library) also works with the .NET Core BCL. This is where the .NET Portability Analyzer comes in.
Migrating from .NET Framework to .NET Core
This post is in a blog series for migrating from .NET Framework to .NET Core. Here’ are some earlier post if you need to catch up:
I’ve recorded a short video that also describes how to use the portability analyzer.
.NET Framework and .NET Core are two entirely different things. Yes, they share the name “.NET”, but they are comprised of different Runtimes and Base Class Libraries. The Base Class Library (BCL) is the foundation of the framework for .NET types such as Object, String, Boolean, Array, List, DateTime, and other primitive types and data structures.
In order to share code/libraries between .NET Framework applications and .NET Core applications is where .NET Standard comes in. I’ve found the simplest way to explain this is to think of .NET Standard as an interface (or a contract).
Both .NET Framework and .NET Core implement that interface.
Because of this, if you write an application that is .NET Standard 2.0 compliant, you can then run your application on both .NET Framework or .NET Core.
.NET Framework 4.8 supports .NET Standard 2.0
.NET Core 2.1 supports .NET Standard 2.0
.NET Core 3.1 supports .NET Standard 2.1
Again, because both .NET Framework 4.8 and .NET Core 2.1 support .NET Standard 2.0, you could run your application on either.
Note that .NET Core 3.1 supports .NET Standard 2.1. And it has been noted that .NET Framework will never support .NET Standard 2.1.
.NET Portability Analyzer
The .NET Portability Analyzer is a tool that analyzes your codebase and provides a report about what types you’re using from the BCL that are supported in .NET Standard or .NET Core.
This allows you to determine how close or far you are away from being able to migrate to .NET Core. A lot of the Types and APIs that exist in .NET Framework also now exist in .NET Core. There are however some gaps that will never be officially implemented in .NET Core. Most notably this will be any type that lives in the System.Web namespace.
Once installed, you can access the settings from the solution context menu:
The Portability Analyzer settings allow you to configure which target platforms to review against. You can specify .NET Core specific or .NET Standard.
After configuring you can then run the “Analyzer Assembly Portability” from the solution context menu. This will generate an Excel file (.xlsx) that will contain all the types that exist in your code that are not supported against the target platforms you specified.
The Portability Summary section gives overall % of each assembly/project in your solution and how it’s supported on the target platform.
The details section lists all the APIs that are missing from a target platform.
The missing assemblies section of the report likely will include a list of dependencies that you are likely using via NuGet. I’ll cover how to handle 3rd party dependencies in another post.
This report will be invaluable for tracking down APIs that you’re using that are not supported in .NET Core.
Once you have these missing APIs in hand that are not supported in .NET Core, it really is up to you to determine how you want to handle or rewrite that code.
If you’re not using Visual Studio, you can simply build the .NET Portability Analyzer and run it against your assembly. You can find the source and instructions on the projects GitHub repo.