I'm compiling my code with net6.0.
One of my depends projects, compiled with netstandard2.0, is using a Nuget package with DLLs for netstandard2.0 and net5.0 (and also netstandard1.1 and net45).
In the output folder, I see it takes the net5.0 DLL.
How can I choose to take the netstandard2.0 DLL?
The requirement is for Dotfuscator, because it looking for the net5.0 Runtime, according to that net5.0 DLL. The question should be asked about Dotfuscator, but I'm in the middle of a research to understand the Dotfuscator better, so in meanwhile I'm asking about the Nuget.
Basically, netstandard2.0 or any of the netstandard targets are actually compiled to netstandard. netstandard isn't a runtime, it's a specification of the API's that are shared between runtimes such as net5, net45, etc, allowing you to use the same codebase over different runtimes. Depending on which runtime you compile for, the netstandard library will be compiled to that.
Basically, since you are compiling your application in net6, your netstandard2.0 library is also compiling as a net6 library. It then will use the correct dependencies (the net5 one) to compile.
Related
This question pertains to .NET 5+, not .NET Framework.
Consider the following dependency tree:
MyWebsite has package references to RandomWebLibrary 1.0.0 and RandomJsonLibrary 2.0.0.
The NuGet package RandomWebLibrary 1.0.0 has a package reference to RandomJsonLibrary 1.0.0.
The NuGet package RandomJsonLibrary has no dependencies.
My questions:
What version(s) of RandomJsonLibrary will be loaded at runtime?
What happens if RandomJsonLibrary 2.0.0 has a completely different API than RandomJsonLibrary 1.0.0?
Can the author of MyWebsite do anything to fix problems that arise from having multiple versions of RandomJsonLibrary in the dependency tree? Is there an equivalent of .NET Framework's binding redirects in .NET 5+?
I'm asking out of curiosity, not because I am encountering a problem. For reference, here is the documentation on Understanding AssemblyLoadContext which seems relevant but did not answer my question.
These questions are answered by the documentation page How NuGet resolves package dependencies. The "Dependency resolution with PackageReference" section is what's relevant to this question since we are talking .NET 5+.
When the package graph for an application contains different versions of the same package, NuGet chooses the package that's closest to the application in the graph and ignores all others. This behavior allows an application to override any particular package version in the dependency graph.
In the example below, the application depends directly on Package B with a version constraint of >=2.0. The application also depends on Package A which in turn also depends on Package B, but with a >=1.0 constraint. Because the dependency on Package B 2.0 is nearer to the application in the graph, that version is used:
To answer the original questions:
RandomJsonLibrary 2.0.0 will be loaded at runtime.
I believe RandomWebLibrary will throw exceptions at runtime if RandomJsonLibrary 1.0.0 and 2.0.0 have incompatible APIs.
The author of MyWebsite can choose the exact version of any dependent package by installing that package directly into MyWebsite. In the case of RandomJsonLibrary, the author of MyWebsite should probably downgrade to RandomJsonLibrary 1.0.0 to prevent RandomWebLibrary from breaking. AFAIK there are no binding redirects in .NET 5+.
I created a NuGet package with an empty logger and my package depends on Microsoft.Extensions.Logging.Abstractions.
My PackageReference line and my dependency in the .nuspec file are set to 1.0.0 with no special syntax which I understand means >=.
My empty logger works just fine with this version and I thought using the lowest working version of the abstractions lib would make my package easier to consume by applications which are likely to have greater versions.
However, when I've referenced my package from an xUnit test project I have a red compiler error CS1705.
My test project references two packages:
The project its testing, which is an ASP.NET Core Razor Pages site that in turn references the same logging abstractions package which is included in Microsoft.AspNetCore.App 2.2.0 metapackage.
My empty logger package, which, in turn references the logging abstractions 1.0.0.0 as I have described.
Compiler error from the test proj says:
Assembly (Razor Pages proj) uses (Abstractions 2.2.0.0) which has a higher version than referenced assembly (Abstractions) with identity (Abstractions 1.0.0.0).
My package can use the higher version so what am I missing?
Edit
Here's a high fidelity diagram ;)
T is "xUnit Proj".
W is the "Website Proj" under test.
E is the empty logger package (Evoq.Instrumentation on nuget.org)
A is metapackage Microsoft.AspNetCore.App 2.2.0
L is Microsoft.Extensions.Logging.Abstractions
There's the nearest wins rule, which I think means that the dependency from T to L via E would win and version 2.0.0 would be used but I'd expect a package downgrade warning not a hard compiler error.
Nearest wins: https://learn.microsoft.com/en-us/nuget/consume-packages/dependency-resolution#nearest-wins
Edit 2
It just occured to me that T > W is a project reference. So maybe that's short-circuiting the NuGet resolution. I'll add a ref from T to A directly and see if that solves it.
I forgot T > W is a project reference. So I think that was short-circuiting the NuGet resolution I was expecting to take place.
I added a ref from T to A directly and that solved it.
Could using nuget packages cause dependency dll hell?
Example im using two nuget packages A version 1.0.0 and B in my class library
B is also heavily dependent on new feature which is non backward compatible version of A 2.0.0.
In such a case wont i get build errors? If thats the case isnt nuget scary?
Following is the very long answer. In nuget dependecies could be private when authoring
https://github.com/NuGet/Home/issues/6614
About the same issue if you are using libraries
https://devnet.kentico.com/articles/referencing-multiple-versions-of-the-same-assembly-in-a-single-application
Short Version
I author a package which targets .NET Standard 1.3 and 1.6. My 1.6 build references System.Runtime.Loader. This package has a placeholder for the MonoAndroid framework, meaning my NuGet package now can not be loaded in Android 7.x projects.
My .NET Standard 1.3 build does not have this dependency. How can I force NuGet to load the netstandard-1.3 build for Android projects, instead of netstandard-1.6?
More Details
When I try and load our current package in an Android 7 project which uses project.json, I see the below error message:
System.Runtime.Loader 4.3.0 provides a compile-time reference assembly for System.Runtime.Loader on MonoAndroid,Version=v7.1, but there is no run-time assembly compatible with win.
My understanding, is that this is caused by the System.Runtime.Loader NuGet package using placeholders for a number of the target frameworks. The structure of this package, is as such:
lib -> netstandard1.5 -> System.Runtime.Loader.dll
MonoAndroid10 -> _._
I also package a netstandard-1.3 build of my package, which does not reference the System.Runtime.Loader assembly. I'm happy for Android users to get the reduced functionality in the 1.3 build - but I can't figure out how to force NuGet to pick this framework, over .NET Standard 1.6.
My current package structure is below:
lib -> netstandard1.3 -> build13.dll
netstandard1.6 -> build16.dll
I've attempted to change it to the below - to force NuGet to pick the more specific target framework, but NuGet seems to prefer netstandard1.6 over MonoAndroid. (I've also tried MonoAndroid10)
lib -> netstandard1.3 -> build13.dll
MonoAndroid -> build13.dll
netstandard1.6 -> build16.dll
Is there any way, as a package author, I can force my downstream users' Android projects to use the .NET Standard 1.3 build of my project, instead of the 1.6 build, which fails to restore due to the placeholder items in the System.Runtime.Loader package?
I eventually found this was a caching problem.
Everything I assumed about how NuGet should work was correct. The issue was that NuGet appears to cache package dependencies, so when I was rebuilding my package, it was still using the old dependency list. Annoyingly, this is only an internal cache - the Visual Studio UI made it appear that my new package's dependencies were being recognised correctly, yet the logs were showing the old dependencies being installed.
The solution was to clear the NuGet cache between each repackage.
I have a library published on nuget that is targeting .NET Framework 4.5
The solution looks like:
Solution
└─ Project.Net45
The nuget package looks like:
─lib
└─net45
I wanted to add a portable class library, so I modifed the solution to:
Solution
└─ Project.Net45
└─ Project.Pcl (Targets Windows Store Apps and Windows Phone 8)
I had to modify the code in the portable class library to work around things that were not compatible with those targets such as the serializable attribute and Thread.Sleep.
The problem is that the portable class library is automatically set to target .NET Framework 4.5 with the following message:
"The following frameworks will be selected automatically because they
support all of the available functionality that is portable between
the other frameworks you have selected: .NET Framework 4.5 and
higher".
and the nuget package now looks like:
─lib
└─net45
└─portable-net45+wp80+win
If I install this nuget package into a library targeting .NET Framework 4.5, I would rather the net45 library be used rather than the portable class library as the net45 library is smaller and doesn't have all the extra code needed to work around the missing features in windows phone 8 etc).
Will changing the nuget package to structure below have the desired effect :
─lib
└─net45
└─portable-wp80+win
Or am I misunderstanding the usage of portable class libraries?
NuGet looks for the best match when adding references to assemblies in the NuGet package.
If your project targets .NET 4.5 then NuGet will install the net45 assembly since it considers that the best match.
The PCL assembly will be used when your NuGet package is added to a PCL project that is compatible.
If you only had the PCL assembly in your NuGet package then NuGet would add a reference to it if you installed it into a project that targets .NET 4.5. However since you have both it will pick the net45 assembly.