Matching Game - sample for porting WinForms application from .NET Framework to .NET Core
About the sample
This is a Memory-style board game application. It contains a
WinForms UI (
MatchingGame.exe) and a class library with the game logic
MatchingGame.Logic.dll), both targeting .NET Framework 4.5. I used this
sample in the blog post to demonstrate how to port the application
project to .NET Core 3.0 and the class library to .NET Standard 2.0.
Step-by-step porting instructions
I suggest doing the migration in a separate branch or, if you're not using version control, creating a copy of your project so you have a clean state to go back to if necessary.
Preparing to port
Install .NET Core 3 and Update Visual Studio to 2019 Preview version (Visual Studio 2017 will only support up to .NET Core 2.2).
Start from a working solution. Ensure the solution opens, builds, and runs without any issues.
Update NuGet packages. It is always a good practice to use the latest versions of NuGet packages before any migration. If your application is referencing any NuGet packages, update them to the latest version. Ensure your application builds successfully. In case of any NuGet errors, downgrade the version and find the latest one that doesn't break your code.
Run .NET Portability Analyzer to determine if there are any APIs your application depends on that are missing from .NET Core. In case there are, you need to refactor your code to avoid dependencies on not supported in .NET Core APIs. Sometimes it's possible to find an alternative API that provides the needed functionality.
PackageReference. If your project uses NuGet packages, you will need to add the same NuGet packages to the new .NET Core project. .NET Core projects support only
PackageReferencefor adding NuGet packages. To move your NuGet references from
packages.configto your project file, right-click on
packages.config-> Migrate packages.config to PackageReference....
You can learn more about this migration in our docs.
Porting main project
Migrate to the SDK-style .csproj file. To move this application to .NET Core, first change the project file to SDK-style format because the old format does not support .NET Core.
Make sure you have a copy of your current
.csprojfile. Replace the content of your
.csprojfile with the following:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>net472</TargetFramework> <UseWPF>true</UseWPF> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> </PropertyGroup> </Project>
For WPF application use:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>net472</TargetFramework> <UseWindowsForms>true</UseWindowsForms> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> </PropertyGroup> </Project>
<GenerateAssemblyInfo>is set to
falsebecause in the new-style projects
AssemblyInfo.csis generated automatically by default which will result in duplication.
The project should build successfully since it is just a new way of writing the same thing.
Move from .NET Framework to .NET Standard or .NET Core. After successfully converting the library to SDK-style format, retarget class library to .NET Standard by replacing this
Build the application. You will get an error "The name 'Registry' does not exist in the current context".
Add Compatibility Pack. Some APIs that are not included in .NET Standard are available in Compatibility Pack. To fix the error add the Microsoft.Windows.Compatibility NuGet package to the project. After installation, the error should disappear.
Install API Analyzer. API Analyzer, available as the NuGet package Microsoft.DotNet.Analyzers.Compatibility, will prompt you with warnings when you are using deprecated APIs or APIs that are not supported across all platforms (Windows, Linux, macOS). If you are using Compatibility Pack, I recommend adding the API Analyzer to keep track of all of API usages that won't work across all platforms.
The class library is migrated to .NET Standard! Now migrate
Add .NET Core UI project. Add a new .NET Core 3.0 UI project to the solution. At this moment, the Visual Studio templates for desktop projects are under development, so I just used the
```cli dotnet new winforms -o <path-to-your-solution>\MatchingGame.Core\ ```
For WPF projects you'd use this:
```cli dotnet new wpf -o <path-to-your-solution>\MatchingGame.Core\ ```
After the new WinForms .NET Core project was created, add it to your solution.
Link projects. First, delete all files from the new project (right now it contains the generic Hello World code). Then, link all files from your existing .NET Framework UI project to the .NET Core 3.0 UI project by adding following to the
<ItemGroup> <Compile Include="..\<Your .NET Framework Project Name>\**\*.cs" /> <EmbeddedResource Include="..\<Your .NET Framework Project Name>\**\*.resx" /> </ItemGroup>
If you have a WPF application you also need to include
.xamlfiles, for example:
<ItemGroup> <ApplicationDefinition Include="..\WpfApp1\App.xaml" Link="App.xaml"> <Generator>MSBuild:Compile</Generator> </ApplicationDefinition> <Compile Include="..\WpfApp1\App.xaml.cs" Link="App.xaml.cs" /> </ItemGroup> <ItemGroup> <Page Include="..\WpfApp1\MainWindow.xaml" Link="MainWindow.xaml"> <Generator>MSBuild:Compile</Generator> </Page> <Compile Include="..\WpfApp1\MainWindow.xaml.cs" Link="MainWindow.xaml.cs" /> </ItemGroup>
Align default namespace and assembly name. Since you're linking to designer generated files (for example,
Resources.Designer.cs) you generally want to make sure that the .NET Core version of your application uses the same namespace and the same assembly name. Copy the following settings from your .NET Framework project:
<PropertyGroup> <RootNamespace><!-- (Your default namespace) --></RootNamespace> <AssemblyName><!-- (Your assembly name) --></AssemblyName> </PropertyGroup>
AssemblyInfo.csgeneration. As mentioned earlier, in the new-style projects,
AssemblyInfo.csis generated automatically by default. At the same time the
AssemblyInfo.csfile from the old WinForms project will be copied to the new project too, because you linked all files
**\*.csin the previous step. That will result in duplication of
AssemblyInfo.cs. To avoid it in
MatchingGame.Coreproject file set
Run new project. Set your new .NET Core project as the StartUp project and run it. Make sure everything works.
Copy or leave linked. Now instead of linking the files, you can actually copy them from the old .NET Framework UI project to the new .NET Core 3.0 UI project. After that, you can get rid of the old project.
Using the WinForms designer for .NET Core projects
WinForms designer for .NET Core projects is not yet available in Visual Studio. However there are two ways to work around it:
You can keep your files linked (by just not performing the step above) and copy them when the designer support is available. This way, you can modify the files in your old .NET Framework WinForms project using the designer. And the changes will be automatically reflected in the new .NET Core WinForms project -- since they're linked.
You can have two project files in the same directory as your WinForms project: the old
.csprojfile from the existing .NET Framework project and the new SDK-style
.csprojfile of the new .NET Core WinForms project. You'll just have to unload and reload the project with corresponding project file depending on whether you want to use the designer or not.