

Code rant: A Framework to DotNet Core Conversion Report
source link: http://mikehadlow.blogspot.com/2020/04/a-framework-to-dotnet-core-conversion.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Background
Motivation
Process
Analysis
-tr
option:asmspy.exe <path to application executable> -tr
Converting Projects to dotnet Standard and Core
.csproj
files, did not go well, so we soon settled on the practice of creating entirely new solutions and projects and simply copying the .cs files across. For this Git Worktree is your very good friend. Worktree allows you to create a new branch with a new working tree in a separate directory, so you can maintain both your main branch (master for example), and your conversion branch side by side. The project conversion process looks something like this:- Create a new branch in a new worktree with the worktree command:
git worktree add -b core-conversion <path to new working directory>
- In the new branch open the solution in Visual Studio and remove all the projects.
- Delete all the project files using explorer or the command line.
- Create new projects, copying the names of the old projects, but using the dotnet Standard project type for libraries, ‘Class Library (.NET Standard)’, and the dotnet Core project type for services and applications. In our case all the services were created as ‘Console App (.NET Core)’. For unit tests we used ‘xUnit Test Project (.NET Core)’, or ‘MSTest Test Project (.NET Core)’, depending on the source project test framework.
- From our analysis (above), add the project references and NuGet packages required by each project.
- Copy the .cs files only from the old projects to the new projects. An interesting little issue we found was that old .cs files were still in the repository despite being removed from their projects. .NET Framework projects enumerate each file by name (the source of many a problematic merge conflict) but Core and Standard projects simply use a wildcard to include every .cs file in the project directory, so a compile would include these previously deleted files and cause build problems. Easily fixed by deleting the rogue files.
- Once all this is done the solution should build and the tests should all pass.
- NuGet package information is now maintained in the project file itself, so for your libraries you will need to copy that from your old
.nuspec
files. - One you are happy that the application is working as expected, merge your changes back into your main branch.
Taking advantage of new dotnet core frameworks
Microsoft.Extensions.*
namespaces.Microsoft.Extensions.*
frameworks, so some refactoring is required to replace these. In the case of TopShelf and Ninject, the scope of this refactoring is limited; largely to the Program.cs file and the main service class for TopShelf, and to the NinjectModules where service registration occurs for Ninject. This makes it relatively painless to do the substitution. With Ninject, the main issue is the limited feature set of Microsoft.Extensions.DependencyInjection
. If you make widespread use of advanced container features, you’ll find yourself writing a lot of new code to make the same patterns work. Most of our registrations were pretty straightforward to convert.log4net
with Microsoft.Extensions.Logging
is a bit more of a challenge since references to log4net
, especially the ILog
class and its methods, were spread liberally throughout our codebase. Here we found that the best refactoring method was to let the type system do the heavy lifting, using the following steps:- Uninstall the
log4net
NuGet package. The build will fail with many missing class and method exceptions. - Create a new interface named
ILog
with namespacelog4net
, now the build will fail with just missing method exceptions. - Add methods to your
Ilog
interface to match the missinglog4net
methods (for examplevoid Info(object message);
) until you get a clean build. - Now use Visual Studio’s rename symbol refactoring to change your
ILog
interface to match theMicrosoft.Extensions.Logging
ILogger
interface and its methods to matchILogger
's methods. For example renamevoid Info(object message);
tovoid LogInformation(string message);
. - Rename the namespace from
log4net
toMicrosoft.Extensions.Logging
. This is a two step process because you can’t use rename symbol to turn one symbol into three, so renamelog4net
to some unique string, then use find and replace to change it toMicrosoft.Extensions.Logging
. - Finally delete your interface .cs file, and assuming you’ve already added the
Microsoft.Extensions.Hosting
NuGet package and its dependencies (which include logging), everything should build and work as expected.
App.config
and System.Configuration.ConfigurationManager
to be replaced with a new configuration framework, Microsoft.Extensions.Configuration
. This is far more flexible and can load configuration from various sources, including JSON files, environment variables, and command line arguments. We replaced our App.config
files with appsettings.json
, and refactored our attributed configuration classes into POCOs and used the IConfigurationSection.Bind<T>(..)
method to load the config. An easier and more streamlined process than the clunky early 2000’s era System.Configuration
. At a later date we will probably move to loading environment specific configuration from environment variables to better align with the Docker/k8s way of doing things.Changes to our build and deployment pipeline
- Using the
dotnet
tool: The build and test process changed from using NuGet, MSBuild and xUnit, to having every step, except the Octopus trigger, run with thedotnet
tool. This simplifies the process. One very convenient change is how easy it is to version the build with the command line switch/p:Version=%build.number%
. We also took advantage of the self-contained feature to free us from having to ensure that each deployment target had the correct version of Core installed. This is a great advantage. - JSON configuration variables: We previously used the Octopus variable substitution feature to inject environment specific values into our
App.config
files. This involved annotating the config file with Octopus substitution variables, a rather fiddly and error prone process. But now with the newappsettings.json
file we can use the convenient JSON configuration variable feature to do the replacement, with no need for any Octopus specific annotation in our config file. - Windows service installation and startup: Previously, with TopShelf, installing our windows services on target machines was a simple case of calling
ourservice.exe install
andourservice.exe start
to start it. Although theMicrosoft.Extensions.Hosting
framework provides hooks into the Windows service start and stop events, it doesn’t provide any facilities to install or start the service itself, so we had to write somewhat complex powershell scripts to invokeSC.exe
to do the installation and the powershellStart-Service
command to start. This is definitely a step backward.
Observations
Microsoft.Extensions.*
frameworks. This was a significant refactoring effort. Doing a thorough analysis of your project and its dependencies before embarking on the conversion is essential. With large scale distributed applications such as ours, it’s often surprising how deep the organisations internal dependency graph goes, especially if, like me, you are converting large codebases which you didn’t have any input into writing. With the actual project conversion I would highly recommend starting with new projects rather than trying to convert them in place. This turned out to be a far more reliable method.dotnet
command), making automated build processes so much easier, is probably the most prominent example. I for one am very pleased we were able to take the effort to make the change.Recommend
-
47
Rantis an all-purpose procedural text engine that is most simply described as the opposite of Regex. It has been refined to include a...
-
17
Metaprogramming, or the ability to inspect, modify and generate code at compile-time (as opposed to reflection, which is runtime introspection of code), has slowly been gaining momentum. Programmers are finally admitting...
-
9
Trump Calls Some Unauthorized Immigrants ‘Animals’ in Rant
-
11
-
17
Why Containers are a Game Changer for Software Development I originally wrote this piece as of part of a paper evaluating container tec...
-
9
Decoupling, Architecture and Teams This article discusses the relationship in software development between code organisation and social o...
-
9
Wednesday, September 05, 2018 The Possibilities of Web MIDI With TypeScript If you’...
-
33
Thursday, January 18, 2018 Configure AsmSpy as an external tool in Visual Studio ...
-
13
Thursday, January 14, 2016 Running The KestrelHttpServer On Linux With CoreCLR
-
11
dotnet core 和 dotnet Framework 启动可执行文件的差别在 Windows 下,使用 .NET Framework 构建出来的应用,可以只有一个可执行文件,在可执行文件里面包含了 IL 代码。而使用 .NET Core 构建出来的应用,将会包含一个 Exe 可执行文件,和对应的 Dll 文...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK