A long path away from the .NET Framework. Part 2 – Process

The previous part is here.

As I wrote before our project has 824 C# projects. As you can imagine it is a lot of source code and converting so much code will take quite a lot of time. But we typically release a new version of our product every month and we definitely will not be able to finish all converting and testing within one month.

As a result, we have to create a branch and periodically merge changes from the main/master branch. But there are the following problems:

  • csproj file is for .NET 6 is very different from csproj file for the .NET Framework
  • if we change some code and this code will be changed for the .NET Framework then we will have to do tricky merges
  • it is a lot of work to convert many projects and we expect that some projects need manual conversion
  • build system needs to be changed as well to be able to build the .NET Core projects

Moreover, at the beginning of this project, we didn’t even know how successful it would be. Imagine we spent a lot of time converting all projects, fixing code, updating all dependencies, changing the build system, etc. Only to find that there is a show-stopper that will cancel this project. Ideally, we would like to find that show-stopper as early as possible.

As a result, we started small. As I wrote before our main executable is a native application and I just changed the code to load .NET 6 runtime instead of .NET Framework. And the application started just fine. Of course, later I found that a lot of things were broken and did not work properly but the application is working in general and it was a big win.

3rd party libraries

A lot of 3rd party libraries work just fine but in some cases, they simply will not work in .NET 6 because they are using classes or methods that simply missing in .NET 6. We needed to switch to the .NET Standard or .NET Core version of that library. But we cannot just update that library in our repository because it will create a lot of other problems because the .NET Framework library will reference the .NET Standard or .NET Core library.

Instead, we decided to add a special step into our build process that simply would copy a different version of that library after everything was built. There is an important note that we are trying to use the same version of that library. For example, if we are using the version of the ABC library for the .NET Framework, we will take version for the .NET Standard to reduce regressions.

There is a temptation to use the latest version but you must resist it. There is a high chance that the API will not be compatible with older versions or that something will work differently. It is best to use the same version.

This solved quite a lot of issues with 3rd party libraries. But not all of them. I must fix some libraries by recompiling some methods directly in dll using dnSpy. One library checked that its runtime is .NET Framework and throws an exception if it is not. It works perfectly fine after I removed that check. Another library was loading native libraries differently for .NET 6 and I had to change it to work the same.

To change a method using dnSpy you need to load that dll, find the correct method, then right-click on it and select “Edit Method”, then change, select “Compile” and save a new Dll.

Just in case, I’m not saying that you should use this method in production but this option is here if you want to test something and don’t want to recompile that library from the source. This method can be used for quick and dirty tests as proof of concept.

How to debug

One unwanted side-effect of that change is that Visual Studio will always start the .NET Framework debugger engine because all projects stay in the .NET Framework format. So if you start the project normally, Visual Studio will immediately display an error and stop debugging.

There are 2 possible solutions. The first is to attach the debugger later because you can select the debugger engine in the Attach dialog. The second is to add the .NET 6 Class Library make it a startup item and set Build Dependencies (right-click and select Build Dependencies).

Also, I recommend enabling all exceptions to see all potential problems so you will not skip some important ones.

Summary for this step

The steps above allow us to get quickly more or less a working product that we can run tests on, run performance tests, and allow our QA to install and manually test. But it was just the beginning.

In the next part, we will go over things that work differently in .NET 6.


Post comment