Solution
Here are some of the frequently asked questions (FAQs) about .NET Core migration:
- Can I use both .NET Core and .NET Framework in the same process?
- Can I execute a .NET Framework assembly in .NET Core?
- What should I do if there are breaking changes between .NET Framework and .NET Core?
- How do I migrate from .NET Framework to .NET Core in Visual Studio?
- How do I deploy .NET Core assemblies?
- How should I handle dependencies in .NET Core?
- How does unloading work in .NET Core?
- What changes should I expect when using System.Windows.Forms in .NET Core?
- How can I expose COM interfaces from .NET Core?
- What are the nuances of Visual Studio projects after migrating to .NET Core?
No, hosting or loading both .NET Core and .NET Framework CLR (Common Language Runtime) in the same process is not supported. You need to use only one version of .NET CLR in an application or process.
Yes, you can execute a .NET Framework assembly in .NET Core, provided the class or function you are using from .NET Framework also exists in .NET Core without any breaking changes.
Note: When using .NET Framework, dependencies of an assembly can exist next to the main assembly or in the GAC (Global Assembly Cache). However, .NET Core does not support GAC, so you need to ensure all dependencies of your assembly are next to it.
This Microsoft Page lists all the major breaking changes between .NET Framework and .NET Core. If you are using any of these, you can do either of the following:
Option 1 - Update your code by using alternatives so that it works in both .NET Framework and .NET Core.
Examples:
- The default value of
ProcessStartInfo.UseShellExecute
has changed between .NET Framework and .NET Core. You can explicitly specify the value for the property in your code so that it works as expected. - In
System.Windows.Forms.ContextMenu
, the type is removed in .NET Core. You can instead use System.Windows.Forms.ContextMenuStrip
that exists in both .NET Framework and .NET Core to make it work. - GAC (Global Assembly Cache) does not exist in .NET Core. Instead of storing dependent assemblies in GAC, you can store them next to your assembly so that both .NET Framework and .NET Core load the dependencies.
Option 2 - Migrate to .NET Core and use alternatives present in .NET Core.
Example: AppDomain
is used in .NET Framework to isolate loaded assemblies. In .NET Core, creating a new AppDomain is not allowed. So, after migrating to .NET Core, you can use AssemblyLoadContext
in .NET Core to isolate loaded assemblies.
You can use the Upgrade Assistant tool in Visual Studio to migrate from .NET Framework to newer versions of .NET. Consider the porting approaches and use the tool to upgrade to the newer .NET version. The tool can update your project automatically and list the issues you need to manually fix.
Typically, the output of building a .NET Framework project will generate an assembly_name.dll
or assembly_name.exe
file.
In .NET Core, your project can generate multiple files, some of which are:
assembly_name.exe
: Generated when you want an executable assembly_name.dll
: Always generated and contains all the logic of your code assembly_name.deps.json
: Contains dependencies of your assembly. This file is generated by the compiler only when needed. assembly_name.runtimeconfig.json
: Contains any requirements on .NET versions. This file is generated by the compiler only when needed. assembly_name.comhost.dll
: Contains any COM interfaces exposed from .NET assembly. This file is generated by the compiler only when needed.
When deploying .NET Core assemblies, you need to ensure all its related files/artifacts (some of which are mentioned above) are deployed.
Unlike .NET Framework, .NET Core does not have a GAC (Global Assembly Cache). You need to ensure that all your dependencies are deployed next to your assembly.
If determining and deploying all direct and indirect dependencies is difficult, you can use the dotnet publish
command to simplify the process and deploy all your dependencies.
In .NET Framework, you can load an assembly in an AppDomain
. Calling Unload
on the AppDomain will forcefully release all the objects and assemblies in the AppDomain.
In .NET Core, AppDomain
does not exist. Instead, you need to use AssemblyLoadContext
. However, Unload
in AssemblyLoadContext
is cooperative, meaning you cannot guarantee that objects and assemblies in the AssemblyLoadContext
will be unloaded. Unloading will not happen if any executing thread can still reach any type or object from the AssemblyLoadContext
.
For more details on unloadability and debugging unloading issues in .NET Core, refer to this page.
To ensure proper cleanup, follow these best practices:
- Call
Dispose
on all .NET objects that implement IDisposable
when the object is no longer needed. - Unregister all event handlers for a .NET object when the object is no longer needed.
- Assign
null
to all ActiveX/COM interfaces, such as TestStand objects, when they are no longer needed.
If you don’t clean up objects properly, they might not be garbage collected. In TestStand, enabling Report Object Leaks
will show TestStand object leaks if objects are not cleaned up correctly.
The default font in System.Windows.Forms
has changed between .NET Framework and .NET Core. Additionally, after migrating to .NET Core, the alignment between controls might look different. Ensure that the look and feel of dialogs and controls are as expected, and update your source code as necessary to achieve the desired appearance.
We encountered issues when using the Anchor
property on all four sides and replaced it with Dock
. Ensure that you verify the resize behavior of your controls.
Unlike .NET Framework, you cannot use regasm
to register COM interfaces in a .NET Core assembly. Instead, you need to add <EnableComHosting>true</EnableComHosting>
to your project file (.csproj
) to generate the assembly_name.comhost.dll
file. You can then use regsvr32
to register the COM interfaces.
For more details, refer to this link.
After transitioning to .NET Core within the Visual Studio environment, it is essential to take the subsequent factors into account:
-
In old-style C# projects (.csproj
) targeting .NET Framework, you need to explicitly add all the files that should be considered for building. However, in .NET Core, C# projects use SDK-style projects, where all the files in the directory and its subdirectories are typically considered for building. To avoid issues, it is better to have separate directories for each project and ensure the project refers only to files in its directory or subdirectories before migrating.
-
The output path might also change, as the default .NET Core output includes the .NET Framework name in its output directory. To retain the behavior of a .NET Framework project, use <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
in your .csproj
.
-
Some macros in Visual Studio projects, such as $(TargetPath)
, will point to assembly_name.exe
when building an executable targeting .NET Framework. However, in .NET Core, the macro will point to assembly_name.dll
. Ensure that you are using macros correctly.
-
Additionally, designers in Visual Studio might not work correctly for DPI scaling. To fix this, add <ForceDesignerDpiUnaware>true</ForceDesignerDpiUnaware>
to your project (.csproj
).