简体   繁体   中英

Mix of .netstandard2.0 and .netframework47 DLLs in nuget package - .netstandard not available

First of all I don't know if what I'm doing is fundamentally the wrong idea, so the answer could be to just not do this, but here goes..

I have a package that I'm creating from a .nuspec file, in its "files" section it contains references to both .net standard and .net framework DLLs. It also has dependencies on both .netstandard20 and .netframework47.

The reason behind this being that the legacy libraries are written in framework, and the new ones are being written in standard.

We have a consuming project (actually, several), written in .netframework 4.7 that needs to address both sets of DLLs. I have changed the project to use PackageReference rather than packages.config, and Visual Studio shows that the package is being referenced (rather than the individual namespaces) in the References section. When I build, the .netframework DLLs get copied into the bin folder, but not the .netstandard DLL.

I've created a scratch project to isolate the issue, and I'm seeing the same there as well. The project basically just refuses to acknowledge the standard one. If I add a using statement into a class with a type from the .netstandard DLL it just puts a red squiggly under it and tells me it doesn't exist in the namespace.

It's been suggested that I break the .netstandard DLLs out into their own NuGet package, is that the answer or am I missing something?

Here's some examples..

The .nuspec file:

<?xml version="1.0" encoding="utf-8"?>
<package >
  <metadata minClientVersion="2.5">
    <!-- other tags removed for brevity -->
    <dependencies>
      <group targetFramework=".NETStandard2.0"></group>
      <group targetFramework=".NETFramework4.7">
        <dependency id="NLog" version="3.1.0.0" />
      </group>
    </dependencies>
  </metadata>
  <files>
    <file src="..\Src\MyProj1\bin\Release\netstandard2.0\NetStandardClass.dll" target="lib\netstandard2.0" />
    <file src="..\Src\MyProj2\bin\Release\NetFrameworkClass1.dll" target="lib\net47" />
    <file src="..\Src\MyProj3\bin\Release\NetFrameworkClass2.dll" target="lib\net47" />
  </files>
</package>

The package is referenced as a package, not the individual types within it:

在此处输入图片说明

If I try to manually add a using statement referencing the NetStandardClass's namespace it's marked as an error

在此处输入图片说明

The package itself contains all 3 DLLs as expected:

在此处输入图片说明

Yet when I build the project and inspect the bin folder, the NetStandardClass DLL is not there: 在此处输入图片说明

It's been suggested that I break the .netstandard DLLs out into their own NuGet package, is that the answer or am I missing something?

Basically, yes, splitting the package is the answer. Another possibility is to move the netstandard assemblies into the net47 folder, or maybe the net47 assemblies into the netstandard2.0 folder, but either way has potential for problems. Although solution is to recompile all your assemblies so they all use the same target framework, but that also has its own issues.

If you had read the NuGet docs and came to the conclusion that what you're attempting should work, please point me to which docs lead you to believe that, so I can update the docs and try to remove the confusion.

The first thing to understand about why NuGet supports a package having multiple target framework folders is because a package might want to use new framework features where possible, but have support for users of the package using older frameworks. Therefore, the package author needs multiple versions of their assembly, depending on which one is compatible with their users project. So, the point is that NuGet needs to select assets from the package depending on which target framework moniker (TFM) the project uses. The way NuGet actually does asset selection is as follows:

NuGet looks are which TFM folders exist in the package, to get a list of TFMs that the package supports. Generially it looks for lib/<tfm>/ and ref/<tfm> folders, but contentFiles/<tfm> and build/<tfm> files might be relevant as well. Notice it doesn't look at any filenames. Which assemblies exist under lib/net47/*.dll or lib/netstanard2.0/*.dll are not yet considered.

Once it has this list of package TFMs, it looks at the project TFM, and it selects the "best" TFM. For SDK style projects that multi-target, it does this once per project TFM. "Best" TFM means same "family" (net* projects always choose net* assets, if available. only when no net* assemblies exist does netstandard compatible .NET Framework TFMs look for netstandard* assets).

Once a package TFM is selected for the project's TFM, then NuGet selects all the files like lib/<tfm>/*.dll , ref/<tfm>/*.dll , contentFiles/<tfm>/<lang>/* , build/<tfm>/<package_id>.<props|targets> , and so on.

So, you can see that if your package contains lib/net47/assembly1.dll and lib/netstandard2.0/assembly2.dll , NuGet will only ever select one of the two assemblies, never both, depending on if net47 or netstandard2.0 is more compatible with the project.

Although it might seem desirable for you if NuGet did TFM selection per TFM, rather than selecting TFM first and then selecting only the assemblies that exist in that folder, consider when a package adds an additional helper utility to "polyfill" old TFMs with features that the package uses in newer TFMs. This helper utility is not needed for newer TFMs, so it would be undesirable for NuGet to select it.

The NuGet team suggests creating one package per assembly, in which case this problem never would have occured, because NuGet would have done asset selection per package, and assuming each package was authored correctly, everything just selects selected correctly. If you insist on bundling all the assemblies in a single package, from my description above I hope you see you need to put all the assemblies in a single TFM folder, but since different assemblies were compiled against different TFMs, there's potential for problems, particularly if some developers are using older versions of Visual Studio that might not support .NET Standard. I very strongly recommend at the very least creating one package per TFM, or recompiling all the assemblies to use the same TFM.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM