Dying to get started with .NET Core but, have a couple lingering .NET framework libraries? Well there is hope. With .NET Standard 2.0, libraries that are compiled for the .NET Framework can be referenced with a compatibility shim. This allows full .NET Framework libraries to be used within .NET Core applications. Don’t believe it? Lets take a look at how this works.
Create a Console Application
The console application is going to be .NET Core so, lets get started with the .NET Core CLI. Running the following commands will create a .NET Core console application and a .NET Standard library.
dotnet new sln --name JrTech.Blogs.Shims dotnet new console --name App --output App dotnet new classlib --name Standard --output Standard --framework netstandard2.0 cd App dotnet add reference ..\Standard\Standard.csproj cd .. dotnet sln .\JrTech.Blogs.Shims.sln add .\App\App.csproj dotnet sln .\JrTech.Blogs.Shims.sln add .\Standard\Standard.csproj
Once these commands are executed, the solution and project files will be created. Looking at the folder structure, we should see something similar to this github commit. The dotnet run command can now be executed to show the classic “Hello World!” response. Awesome.
Add a .NET Framework Library
Now that the .NET Core project is up and running, lets open the solution in Visual Studio 2017 and add a .NET Framework library, as shown in the next commit. Of course, this is done for the sake of an example. This situation would typically occur with legacy .NET Framework libraries. If we were creating a new libraries, we would want to create them using the Class Library (.NET Core) or Class Library (.NET Standard) project template.
What is the Class Library (Legacy Portable) template? You can read about it here but, as mentioned in the article, it should not be used.
Because Portable Class Library projects target only a very specific subset of .NET implementations, we strongly discourage their use in new application development. The recommended replacement is a .NET Standard library, which targets all .NET implementations that support a specific version of the .NET Standard. For more information, see .NET Standard.
Adding Console Output
The projects should be empty at this point but, we will be changing that. Adding a couple classes to log to the console will help give some context regarding the code that is getting executed. To start, a FullUtil static class is created which contains a single Run function.
The .NET Standard library will need to reference the .NET Framework library. This is the whole point of the article :). The reference can be created from either the .NET Core CLI or from Visual Studio. Next a similar class is created in the .NET Standard library. Note, the FullUtil is being called!
With the class libraries ready, they can now be used within the console application. See the Program.cs implementation below. Within the Main method, StandardUtil.Run() is called which in turn calls FullUtil.Run(). The FullUtil class cannot be used DIRECTLY within the console application. A .NET Core assembly cannot directly reference a .NET Framwork assembly. This is where the .NET Standard library does something AWESOME for us and acts as a compatibility shim between the two frameworks.
Running the Application
The console application can now run in Visual Studio OR with the dotnet run command. With both approaches, we get the following output.
Hello from .NET Core! Hello from .NET Standard! Hello from .NET Framework!
If that wasn’t enough, you can take advantage of even more when using a compatibility shim. Not only do you get the ability to utilize .NET Core’s performance improvements but like any other .NET Core application it will run on Windows, Mac, or Linux. That’s right, you can run a .NET Framework library on Mac or Linux. In fact, here if a screenshot of our sample application running on my MacBook!
How does this work?
The magic lies with the netstandard.dll being used as a facade to type-forward references to the appropriate assembly. For more information, you can see the Microsoft documentation on github which explains in more detail how this works.
With the netstandard.dll acting as a facade, what happens if a referenced assembly uses a platform specific feature? Compilation error? Runtime Exception? We can find out by making a few modifications to the sample project. Building off of what was highlighted in the Microsoft documentation, lets make use of Microsoft.Win32.Registry class. Querying the registry, we can retrieve the Windows version and add it to our console output.
Running our console application now gives us the following output.
Hello from .NET Core! Hello from .NET Standard! Hello from .NET Framework on Windows 10 Enterprise!
Now not only does MacOS not have a windows version but, it doesn’t have a registry at all! With the latest round of changes, lets again run the application on my MacBook. As expected, we get a PlatformNotSupportedException.
While the compatibility shim is very cool, it should be used as a last option. When building an application in .NET Core, shareable libraries should be written in .NET Standard as a first option. Legacy .NET Framework libraries should be ported over. In cases where source code is unavailable or a library cannot be converted to .NET Standard, a compatibility shim may be an option. That said, it is important to understand that it is possible to encounter PlatformNotSupportedExceptions when running on Linux or MacOS.