Friday 6 March 2020

1 - Migrating to .NET Standard

.NET Standard 2.0

Blazor client apps and libraries need to reference .NET Standard 2.0 code - and more recently they have been based on .NET Standard 2.1.

.NET Standard 2.0 is fine as you can use code targeting this in .NET Framework applications of version 4.7.2 or later, but this isn't the case for .NET Standard 2.1 - it only works with .NET Core 3.0 onward. You can see the compatibility table at https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support

However as a migration strategy I recommend for non-Blazor libraries to use 2.0 during the migration process.

Migration Strategy

Existing applications using .NET Framework are rarely easy to just migrate in a big-bang approach where you upgrade everything to .NET Standard 'overnight' and the application is now .NET Standard/Core. Usually they are comprised of multiple modules and would take several weeks or even months to update. So does this mean it's impossible?

Well, my approach during 2018 and 2019, while Blazor was under development was that we would plan to migrate to .NET Standard for all libraries.

The first thing we did was to start now by writing all new libraries using .NET Standard 2.0 where possible. This ensures we don't continue to build a bigger workload of migration as we go forward.

Next was to gradually migrate lower-level libraries to .NET Standard 2.0 as and when we could.

Issues

While .NET Standard 2.0 claims compatibility with .NET Framework 4.6.1, please note the asterisk in the compatibility table. There are lots of problems you'll encounter if you're using anything less that .NET Framework 4.7.2 (I did!).

Migrating to .NET Standard

Our first hurdle was that we needed to modify all our own libraries from .NET Framework 4.7.2 to use .NET Standard 2.0 instead.

As .NET Standard 2.0 is very closely aligned with the Framework functionality, there should be relatively few code changes required, but anything platform specific has to be avoided.

This might sound like a simple task but remember that a .NET Standard library can only reference other .NET Standard libraries - so you have to start at the bottom of the dependency tree and work your way up. If you have a small number of packages this should not be an issue, but it can take a lot of work for larger applications.

Assumptions

I realised writing this post that I'm making some big assumptions here, and if these don't apply you're probably not in a good place to undergo this process.

The first is that everything is under source control. Git, AzureDevOps, VSTS, (SourceSafe even!) whatever - if you don't have a way to revert stuff you're going to have problems. I'm not expecting anyone reading this type of article not to have source control, but it's worth pointing out.

Second is that you have reasonably comprehensive unit tests in some form. If your app is quite large (mine has over fifty different libraries included in the main web app) then this is a must. You're about to make a lot of major changes and you need to know that each component you migrate to .NET Standard will still work correctly. I actually upgraded my unit tests to .NET Core 3.0 as part of the process but you can keep them on .NET Framework during the transition to give you confidence that you're not going to break things.

Pitfalls

I decided to do a step-by-step approach by initially updating the lowest level utility libraries to .NET Standard 2.0, and leaving the higher up libraries in .NET Framework for now. This is where I ran into our first issues.

Microsoft's compatibility matrix states that .NET 4.6.1 implements .NET Standard 2.0, but note the  2 suffix:

...there are several issues with consuming .NET Standard libraries that were built for those versions from .NET Framework 4.6.1 projects. For .NET Framework projects that need to use such libraries, we recommend that you upgrade the project to target .NET Framework 4.7.2 or higher.

So you really need to have your application upgraded to at least .NET Framework 4.7.2 to be sure it will be able to consume .NET Standard libraries without a lot of issues. I did encounter these and I upgrade our app to use 4.7.2.

So if you're not on at least 4.7.2 you need to first do an upgrade to that, and ensure everything works on this version first, before attempting the move to .NET Standard.

References and Project Formats

The other changes that are not so obvious are that .NET Standard and .NET Core use the newer, leaner "2017" project format. In addition, the project Nuget references no longer use a packages.config file but have the references embedded in the project file. Both of these changes need to be implemented to make migrating to .NET Standard possible.

References Upgrade

If you've still got packages.config the first step is to upgrade to Package References. The latest versions of Visual Studio support this as a simple click-to-migrate operation. See https://docs.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference for instructions on how to do this. Once this is done, the packages folder is gone, the packages.config files are removed and the project files now contain the package reference list.

Project Format Upgrade

The second step is to upgrade the project format to the 2017 format version. This is a much leaner, simpler format and is able to host most types of library, but not all. For example, a .NET Framework 4.7.2 class library can use the 2017 format, but an ASP.NET web application cannot.

However in this situation you're upgrading libraries so that should not be an issue.

Upgrading is not a simple process, but there is an excellent tool https://github.com/hvanbakel/CsprojToVs2017 which does most of the work. This is installed as a dotnet extension and you upgrade using the command line.

dotnet migrate-2019 wizard [solutionfilename.sln] 

You can optionally chose to migrate on a project-by-project basis if you wish.

The tool converts the content of the .csproj file to the 2017 format, including the Package References settings. It also copies values out of the AssemblyInfo.cs file in the Properties folder. The file is retained and only has a couple of remaining values, ComVisible and Guid - if you don't need these you can remove this completely.

Now the projects are upgraded you can open and edit the .csproj file by double-clicking it. You'll find the files much smaller as the new format uses an include-by-default approach for files, so it no longer needs to list each file in the project. This really helps with source control conflicts when different people work on the same library!

You can also delete a lot of the cruft that gets copied over. Nate McMaster has an excellent article that covers the migration project files in much more detail.

Nuspec

If you had a .nuspec file in a project to define NUGET packaged details this can also be dispensed with - the 2017 format contains all the values you need and you can use dotnet pack to create a package. I will discuss the nuget package process for build servers in a later post.

No comments:

Post a Comment

5 - Adding Blazor

So in the previous step 4, we had upgraded our application to ASP.NET Core 3.1, but we still had no actual Blazor anywhere in the system. Ho...