Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you import a package targeting full framework into an ASP.NET Core 3+ application?

My understanding is that, starting with ASP.NET Core 3.0, .NET Framework is an unsupported target framework, and thus you can only run on the .NET Core runtime.

If this is the case, what NuGet packages can be imported into an ASP.NET Core 3 app?

I assume that you could reference any package that targets netstandard, but what about packages that only target the full framework (i.e., a legacy package that only targets net45)?

What happens if the package you import references an assembly that's not part of .NET Core—i.e., System.Drawing?

like image 833
kimsagro Avatar asked Mar 02 '23 08:03

kimsagro


1 Answers

TL;DR: You can still reference (packages which depend upon) .NET Framework assemblies from .NET Core 3 and even .NET 5, but you will receive a runtime error if you call into any code which relies upon APIs or libraries not (yet) supported by .NET Core. You can discover these using Microsoft's .NET Portability Analyzer


Background

First off, you're correct that ASP.NET Core 3.x applications can no longer target the .NET Framework, as announced by Microsoft in 2018. That capability previously allowed ASP.NET Core applications to call into .NET Framework libraries, and thus offered an intermediate solution for web applications migrating to .NET Core.

Note: Since the .NET Framework only runs on Windows machines, writing ASP.NET Core web applications which targeted the .NET Framework implicitly restricted those applications to running on Windows.

Behavior

Even when targeting .NET Core or now .NET 5, however, you're still able to reference .NET Framework packages and assemblies, assuming you're on a Windows machine and have the corresponding .NET Framework installed. The inner workings of this are a bit involved, but the short of it is that .NET Core and .NET 5 will evaluate .NET Framework assembles as though they are .NET Standard assemblies. If the API call is also implemented in the .NET Core runtime, it will work fine—but if the API call is exclusively part of .NET Framework, you'll receive an exception.

Surprise! It's really important to emphasize that this is a runtime exception. You will still be able to reference the .NET Framework assembly, write calls to problematic members, and compile your code without any warnings. But as soon as you call into code dependent on a .NET Framework-specific assembly, you'll receive the runtime exception.

Example

With .NET 3.0, a significant portions of .NET Framework libraries have been ported over to .NET Core. In fact, this includes most of the System.Drawing libraries you referenced as an example—though there are good reasons you may not want to use them. If you dig a bit deeper, however, there are plenty of libraries which remain unsupported. One obvious example is the WebConfigurationManager, which could be used to access configuration settings from web.config files.

.NET Framework Code

So, as an example, let's say you have the following function in a .NET Framework class library, which returns an array of keys from your web.config's <AppSetting>s element:

public static class Configuration
{
    public static string[] GetAppSettings() => System.Web.Configuration.WebConfigurationManager.AppSettings.AllKeys;
}

ASP.NET Core Code

And then, in an ASP.NET Core controller, you expose an endpoint to retrieve this data:

public class MyController: Controller 
{
    public IActionResult ApplicationKeys() => Content(String.Join(", ", Configuration.GetAppSettings()));
}

Exception

In an ASP.NET Core 2.x application targeting the .NET Framework, this will work just fine. In an ASP.NET Core 3.x or ASP.NET Core 5 application, however, you'll receive the following runtime error when you call the /My/ApplicationKeys/ route:

System.TypeLoadException: 'Could not load type 'System.Web.Configuration.WebConfigurationManager' from assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.'

Avoiding Surprises

If you're anything like me, this will make you incredibly nervous. You'd much rather receive design-time errors—or, at least, compile-time warnings—as soon as you attempt to call into a library relying upon unsupported code. Fortunately, Microsoft offers a .NET Portability Analyzer, which is also available as a Visual Studio Extension, for exactly this purpose.

As of .NET 5, there's also a compatibility analyzer built into the SDK which will identify calls that are not supported by the .NET 5 runtime on particular platforms. This requires that target libraries explicitly annotate their types with the [SupportedOSPlatform()] attribute, so you won't get any warnings for legacy .NET Framework types. But this will help identify similar types of compatibility issues for libraries targeting a variety of platforms.

Example

If you run the Portability Analyzer on the above sample code, for example, it will output an Excel spreadsheet identifying that T:System.Web.Configuration.WebConfigurationManager is Not Supported in e.g. .NET Core,Version=v3.1 or .NET Standard + Platform Extensions,Version=v2.0.

Note: Microsoft used to offer an API Analyzer as a NuGet package, which promised to provide design-time analysis in Visual Studio. Unfortunately, the code hasn't been updated in two years, and the latest release is 0.2.12-alpha. In my evaluation, it was not effective at identifying issues.

Sample Project

I've put together a sample project on GitHub which demonstrates the above behavior. It includes the following projects:

  • ASP.NET Core 2.0 Website targeting .NET Framework 4.8
  • ASP.NET Core 3.1 Website targeting .NET Core 3.1
  • .NET Framework class library with calls to the legacy WebConfigurationManager

Both ASP.NET Core websites include two endpoints which call into the same .NET Framework 4.8 class library. The first is a "Hello world" example which will execute fine on both projects, since it relies exclusively on common APIs:

http://localhost:5000/Basic/Index

The second will fail on the ASP.NET Core 3.1 project, since it calls into the legacy WebConfigurationManager API:

http://localhost:5000/Basic/Configuration

Disclaimer: This is a quick and dirty repository that I put together to verify my understanding prior to posting this. If there's interest, I'll tidy it up and document it. For now, however, it may prove useful for those of you who need to see this in action.

Acknowledgments

@Chris Pratt offered an excellent answer covering similar material last year. It's worth reading.

like image 179
Jeremy Caney Avatar answered Mar 05 '23 15:03

Jeremy Caney