Using .NET Standard with Full Framework .NET

Using .NET Standard with Full Framework .NET

.NET Standard has been around long enough now that most people are reasonably familiar with this somewhat 'unnatural' concept. The idea of targeting or consuming a library that is not really a library but a specification which in turn affects the build process and runtime binding takes a bit getting used to.

Things have gotten a little clearer recently with better documentation from Microsoft and clearer designations on what versions of the various .NET runtimes support what version of .NET Standard. With more implementations out in the wild now too, it's easier to see and realize the benefits of .NET Standard whereas in the early days of .NET Core and .NET Standard much of .NET Standard seemed sort of academic.

But there's still a lot of confusion for people who are not keeping up with all the latest .NET tech. It's not a concept that comes naturally unless you've been following the evolution of .NET and it's torturous versioning paths. Rather, seeing it in action is the best way to make sense of it - at least that's how it worked for me.

I've talked about .NET Standard in previous posts (and here) so I won't rehash it here. This post is about a more specific scenario which is using .NET Standard libraries in full .NET Framework, which has its own set of peculiarities.

But first a short explanation of .NET Standard.

 

Stuck in 4.6.2

Markdown Monster has been running with a target framework of 4.6.2 in order to support older runtime installs on Windows. Supporting 4.6.x still brings in quite a few people who haven't updated to Windows 10 mostly, and even for my developer centric audience that's a not-insignificant number of users. I use 4.6.2 specifically because it introduced some important high DPI improvements for WPF that Markdown Monster relies on (for its built-in screen capture feature).

At the time I decided to give updating LibGit2Sharp with the .NET Standard based 0.25.4 version a try in my 4.6.2 project and I got the following assembly hell:

 

 

By adding a reference to a .NET Standard 2.0 package a huge number of support assemblies - a subset of the CoreFx libraries effectively - are being pulled into the project, which is ugly to say the least.

This was such a crazy mess that nearly doubled my distribution size, so I decided to not roll forward to the 0.25 version of LibGit2Sharp.

The issue here is that .NET 4.6.2 is .NET Standard compliant but in order to work, it needs a ton of newer features that were not present when that version of the framework shipped. In a way .NET Standard was bolted拴住 onto .NET 4.6.1, 4.6.2, 4.7, 4.7.1 resulting in all those extra assemblies and assembly redirects in app.config.

If you really need to use a component that doesn't have a .NET Framework version this might be an option, but frankly the distribution overhead made that a non-starter for me.

Thanks, but no thanks!

 

What a difference a Runtime Makes

There's a way to make this pain go away by targeting .NET 4.7.2 which as I mentioned earlier is fully .NET Standard Compliant. This means that all those APIs in the .NET Standard DLLs that were pulled in for 4.6.2 to provide missing functionality are available in the shipped .NET 4.7.2 base runtime.

The end result is: No extra dependencies. Here's the same bin\Release output folder in the 4.7.2 project with the same dependency added to Markdown Monster targeting 4.7.2:

 

 

As you can see there no extra System. dependencies except the ones I added explicitly to the project.

Much Better!

 

LibGit2Sharp has added back a 4.6 Target

An even cleaner solution is the route that LibGit2Sharp took eventually by bringing back a .NET 4.6 target in the library: In version 0.26 - conveniently after I went through all of the experimentation described above - you now have both .NET Standard and .NET 4.6+ target support:

 

This multi-targeted package when added to a full .NET Framework project will use the NET46 assemblies and not clutter up your project with all those extra dependencies even on my original .NET 4.6.2.

And I'm back to being able to keep up with the latest versions of LibGit2Sharp in Markdown Monster.

We have a Winner!

I think that this was a smart move on LibGit2Sharp's part. It's in vogue时尚,流行,时髦 to .NET Standard All the Things, but practicality and more than likely the sheer绝对的,透明的 numbers of user base often speak louder than the latest fad时尚. I was not planning on upgrading LibGit2Sharp to the .NET Standard only version, because of the DLL dependencies - that's enough of a blocker for me. Although I tried out running on .NET 4.7.2 which didn't add the extra assembly load, that doesn't really help me because 4.7.2 still excludes too many people from my user base without a forced .NET Framework upgrade which otherwise offers minimal benefits especially to end users.

 

Library Authors: Don't target only .NET Standard if you work on Full Framework!

If you are a library author, multi-targeting in the newer .NET SDK Style project format is fairly easy to set up and assuming your library doesn't depend on some of the newest features that are in .NET Standard that didn't exist previously, there are usually no code changes required to compile both for .NET Standard or Full Framework.

For the time being I think any popular 3rd party library that is expected to work on full .NET Framework, should continue to ship a full framework target in addition to .NET Standard.

Regardless I suspect we're likely to see more and more libraries that end up targeting only .NET Standard. Hopefully the runtime version counts will keep creeping up to meet the versions that support .NET Standard completely. Maybe when .NET 4.8 ships the 1 step back will be 4.7.2 at that time.

Summary

.NET Standard with full framework is still confusing because it's not all that obvious what dependencies will be pulled in when bound to a specific version of the full .NET Framework. I hope this post clarifies some of that.

To summarize here are the key points

  • .NET 4.6.1-.NET 4.7.1: Not nyet!
    4.6.1 through 4.7.1 add a boatload of additional runtime assemblies and assembly redirects to your project to work with .NET Standard 2.0. Avoid, unless you really need to use a .NET Standard component. Look for older versions that do support full framework. Pester third parties to still provide .NET Framework targets which is not a difficult thing to do with SDK style projects.

  • .NET 4.7.2: Works as advertised
    .NET 4.7.2 is the first version of .NET Framework that fully supports .NET Standard 2.0 and there are no additional assemblies dumped into your output folder. This is what you would expect to happen.

  • Multi-Targeting for libraries is still recommended
    Because of the limited 'full .NET Standard support' in older version of the .NET Framework, it's still recommended for third party providers to ship .NET Framework targets with their NuGet packages in addition to .NET Standard.

    Multi-targeting with the new SDK projects is easy and once configured doesn't require any additional work in most cases. Using this approach full framework target can avoid the DLL deployment nightmare on 4.6.1-4.7.1.

  • If possible use .NET 4.7.2 or later
    If you want full .NET Standard support, consider using .NET 4.7.2 or later. Not always an option, but if you can, this is the cleanest way to .NET Standard 2.0 o full framework today. We just need to wait until 4.7.2 or more likely 4.8 gets into the Windows update pipeline to flush out the old versions.

Going forward it's pretty clear that Microsoft is gearing up for .NET Core even for the desktop. With .NET Core 3.0 and desktop framework support everything is likely to go to .NET Standard at some point.

But for now, and probably the foreseeable short term, we'll have to live with this mish mash of support, and I hope this article has shed some light on what you can expect when you use .NET Standard on the full framework.

 

posted @ 2020-04-03 17:52  ChuckLu  阅读(256)  评论(0编辑  收藏  举报