10 March 2017
Sharing libraries between .Net Core and .Net Framework applications
If you make any significant commitment to .Net Core there is a fair chance that you’ll need to share some libraries with .Net framework applications. After all, .Net Core is an emerging ecosystem that still has a lot of missing pieces.
In a mixed ecosystem you have two options for sharing libraries. Firstly, you can develop a .Net Standard library that can be shared directly between applications so long as the versions match up. Alternatively, you can use multi-targeting to cross-compile a library for more than one platform.
Sharing through the .Net Standard
Microsoft introduced the .Net Standard to provide a common standard for APIs across the Microsoft ecosystem. It can be seen as a successor to portable class libraries (PCLs) that simplifies the business of targeting different platforms. Where PCLs are based on profiles defined by intersecting platform capabilities, the .Net Standard provides a curated set of APIs.
The upshot is that you can create libraries that can be directly referenced by both .Net Framework and .Net Core applications (and Xamarin too eventually). You just need to ensure that the .Net Standard Library NuGet package is added to any .Net Framework application that wants to reference a .Net Standard library.
Given that there have been more than half a dozen versions of the .Net Standard already it’s not always immediately clear which version to target. It can feel like instead of suffering DLL hell, we now have .Net Standard hell. In general, the higher the version of .Net standard then the wider range of APIs that will be available to you. The lower the version the wider range of platforms you’ll be able to support.
As ever with any .Net Core migration, you are the prisoner of your dependencies. Although there is increasing adoption of .Net Standard among commonly-used libraries, the support for anything below version 1.3 is pretty sketchy. In practical terms, this can tie you into a minimum .Net Framework version of 4.6.
Most development shops have a mixture of .Net framework versions in production. This can make the adoption of .Net Standard libraries quite difficult, particularly in larger ecosystems. You’d be surprised how much .Net 4.0 or even 3.5 is lurking in some dark corners. Even version 4.5.2 may still be supported by Microsoft, but it is limited to version 1.2 of the .Net Standard.
For messier ecosystems that cannot guarantee a more recent vintage of the .Net framework, multi-targeting can help to widen the reach of shared libraries. This allows you to compile a single project natively for both .Net Standard and your preferred version of the .Net Framework, though it does come at a cost of having to manage multiple sets of compiled output.
This has become a lot more straightforward since Visual Studio 2017 was released. Multi-targeting was a pretty choppy experience in Visual Studio 2015 using the old JSON-based xproj format. You could get a project to compile for more than one framework, but you were not able to use a direct project reference in a .Net Framework project. This required some pretty difficult workarounds: you either had to migrate the target project to the xproj structure or distribute the dependency using NuGet. Neither approach was ideal.
This has been addressed in the new tooling, though you do still have to manually edit the project files to get it working. Re-loading the solution is also recommended after making any manual changes to the frameworks.
The example below shows how you would adjust the TargetFramework element to get a project to compile for more than one framework:
<PropertyGroup> <TargetFrameworks>net452;netstandard1.3</TargetFrameworks> </PropertyGroup>
When you compile this project you will see two outputs in the BIN folder, one for each of the frameworks specified. This allows you to create project references to the shared assembly, both for projects built with .Net Framework 4.5.2 or anything built on .Net Core 1.0.
Migrating existing libraries
When planning shared libraries bear in mind that the .Net Standard is based on a subset of the overall APIs currently available in the .Net Framework. Many of the APIs that you may have grown accustomed to using in the .Net Framework will not be part of the .Net Standard, particularly once you get into those areas that have been most heavily refactored by .Net Core (e.g. ASP.Net).
Implementing existing .Net Framework libraries as shared .Net Standard libraries inevitably requires a migration, similar to porting to .Net Core. Microsoft’s API Port tool can tell you how much work this will involve. It is a command line tool that gives you a detailed breakdown of the types and members that will cause compatibility issues. In some cases, they will require a particular version of the .Net Standard, while others will not be supported at all.
Once again, the greatest difficulties are likely to be around external dependencies. Multi-targeting will only work if all the dependencies in the project also support your target frameworks. Some implementation differences can be smoothed over with conditional compilation statements, but one of the main intentions of the .Net Standard is to eliminate the need for this kind of workaround.
Coming soon… unification and the compatibility shim
The whole issue of sharing code should be made easier by version 2.0 of .Net Standard. This promises to be a version that finally provides a single set of APIs that can be applied across .Net Core 2.0, .Net Framework 4.6.1 and Xamarin. It’s also promising a compatibility shim that will allow .Net Framework assemblies to be used directly by .Net Standard libraries. The catch here is that this won’t work for older framework versions or assemblies that use APIs that are not supported by .Net Standard.
Despite this limitation, Microsoft think that more than 60% of the most commonly used .Net Framework packages on NuGet will be compatible with .Net Standard via the shim. This should make it easier to make a commitment to .Net Core as many legacy .Net Framework libraries will be accessible to via .Net Standard.
The stuff that will never be shared
Despite the growing compatibility promised by .Net Standard 2.0 there are still some areas that will never be part of the .Net Core world. Windows Forms and WPF in particular will forever remain in the .Net Framework as Windows Universal applications have become the new UI creed. Anything that relies on platform-specific features will also be excluded. For example, you cannot build a Windows Service using .Net Core and are expected to use a platform-specific process manager (e.g. NSSM for Windows or Supervisor for Linux) rather than a project template or library like TopShelf.
The fate of WCF is a little more ambiguous. There is still a team working on it, but any port to .Net Core seems to be forever on the backburner. This is pretty bad news for anybody who has made a significant investment in WCF for their communications infrastructure. This can create an architectural barrier to adoption which will be much trickier to negotiate than a bunch of .Net Framework dependencies.