When you have a big project in .NET full framework and you want to convert to .NET Standard/Core, usually MultiTargeting can be a viable solution to avoid a Big Bang conversion. You start with the very first assembly in the chain of dependency, the one that does not depend on any other assembly in the project. Then, you can check compatibility with .NET standard for all referenced NuGet Packages. Once the first project is done, you can proceed with the remaining steps in the process.
The very first step is converting all project files to new project format, leave all project to target full framework, then you can use a nice technique called MultiTargeting starting with the aforementioned first assembly of the chain.
To enable it, edit the project files, and change TargetFramework to TargetFrameworks (mind the finals) and specify that you want that project compiled for Full Framework and .NET Standard. Including .NET Standard in the list of target frameworks requires removal of all code that is dependent on the Full Framework, but this is not always obtainable in a single big bang conversion, because the amount of code could be really high.
Multitargeting in action
To ease the transition, I usually add some constants, so I’ll be able to use the #ifdef directive to isolate some piece of code only when the project is targeting Full Framework.
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
<PropertyGroupCondition=" '$(TargetFramework)' == 'net461'">
After multitarget is enabled and netstandard is one of the targets, the project usually stops compiling because it usually contains some code that depends on the Full Framework. There are two distinct problems:
You have NuGet packages that do not support netstandard.
There are references to Full Framework assembly.
To solve the problem, you must use conditional referencing, setting the references only for a specific framework version.
Conditional reference in action
As you can see in the figure above, I can reference different NuGet packages or assemblies depending on the version of the framework used. The nice part is being able to reference different versions of a library (e.g. System.Buffers) for Full Framework or .NET Standard Framework.
At this point, the project usually stops compiling because the code references classes that are not available in .NET Standard. For example, you can verify that the .NET Standard misses references like System.Runtime.Caching and System.Runtime.Remoting. For all the code that uses references not available for .NET Standard projects, just use a #ifdef NETFULL to compile those classes only with the Full Framework. This is not a complete solution, but, in the end, you will have your project that is still fully functional with the .NET Full Framework and a nice list of #ifdef NETFULL that identifies the only parts that are not available in .NET Standard. Now, you can continue working as usual, while slowly removing and upgrading all the code to .NET Standard.
Now, repeat the process for every project in the solution. Usually, there are two scenarios:
The vast majority of the code is Full Functional with .NET Standard, and there are very few points in the code where you use #ifdef NETFULL.
Some of the classes/functionality only available in the Full Framework are extensively used in all of your code; the amount of code with #ifdef NETFULL is becoming too big.
The first point is good. You can continue working with the Full Framework. Then, convert the remaining code at your pace. If you find yourself in the second scenario — as an example, if some code uses MSMQ —you should isolate the Full-Framework-dependent code in a single class. Then, use Inversion Of Control to inject the concrete class. As an example, instead of having multiple points in the code that use MSMQ, simply abstract all the code in an IQueue interface and create an MSMQQueue class, and you have a single point of code that is not available for .NET Standard. You can then write code that uses Rabbit MQ, and the problem is gone with a simple class rewrite.
Let's do an example: I have an InMemoryCacheHelper to abstract the usage of MemoryCache. Since MemoryCache class from System.Runtime.Caching is not available in .NET Standard, I simply protect the class with #if NETFULL conditional compiling.
Conditional compilation with .NET Full
Looking in the documentation, there is a NuGet package called Microsoft.Extensions.Caching.Memory meant to be a replacement for the MemoryCache standard Full Framework class. I can use these NuGet packages to create an implementation of InMemoryCacheHelper compatible with .NET Standard.
Remember that you need to reference the Full Framework version (1) for net461 compilation, but reference NuGet package for the .NET Standard version. This can be done manually editing references in csproj file in the following figure:
Referencing the correct assembly or NuGet Package
Now, you can come back to the InMemoryCacheHelper class, add an #else branch to the #ifdef NETFULL directive, and start writing a version of the class that uses Microsoft.Extensions.Caching.Memory. You will have all of your code that is able to target both .NET Full and .NET Core. You can rewrite the entire class, or you can use the same class and use #if NETFULL inside each method. I prefer the first approach, but this is what happens when I edit the .NET Standard version of the class.
No highting or IntelliSense
Because we are in a branch of conditional compilation that evaluates to false, VS does not offer highlighting or Intellisense. At this point, you should be aware that when you use multitargeting, the order of the frameworks in the project file matters. The first framework in <TargetFrameworks> node is the one that VS would use to evaluate conditional compilation. This means that when you are working with code that should target both the Full Framework and .NET Standard, you need to change the order to fit your needs.
In this example, I needed to change <TargetFrameworks>net461;netstandard2.0;</TargetFrameworks> to <TargetFrameworks>netstandard2.0;net461</TargetFrameworks> to save the project file and unload and reload the project (sometimes you need to force a project reload) and Visual Studio will consider .NET Standard2.0 during code editing.
Reversing the order of frameworks
Now, you can edit the .NET Standard version of your class and convert one by one all parts of the code that does not natively run on netstandard.