Example:
Detected package downgrade: Microsoft.Extensions.Localization from 1.1.2 to 1.1.1
I don't understand the reasoning for the downgrade, when 2nd dependency allows for the version 1.1.2
I would expect the downgrade to happen, if I used a version range of type "(,1.1.1]"
Current state forces the developer to manually update the directly referenced version of package, even when the referenced version range says "at least 1.1.1, but can be higher".
If there's a discussion which explains benefits of the current approach, please provide me with a link.
That's the nature of the beast. Why are you directly referencing a lower version? Ancestor packages eclipse ones further down the chain.
Current state forces the developer to manually update the directly referenced version of package, even when the referenced version range says "at least 1.1.1, but can be higher".
This is true. Why not just use the transitive dependency? The other reason for this behavior is to meet developer expectation. When you reference a package directly, it would be pretty odd if it didn't prefer that version.
That said, I can see why it would be confusing (it's new behavior).
As @davidfowl mentioned removing the top level dependency and relying on the transitive dependency will give you the behavior you are expecting here.
Restore allows the nearest dependency to override to give the project author better control over dependency packages. If for example another package brought in a higher version of a package that had an issue, or you did not want to use that version, you could reference a lower version directly and get that version instead.
At that point it is up to you to ensure that the downgrade does not cause problems for your application which is why the warning is shown.
Thank you both for your answers.
Why are you directly referencing a lower version?
At the time of writing my original post, I assumed, that there was at first just a direct reference to .Localization, and that later on, .Mvc was added.
Now, as I write this, I realize that for Mvc and Localization it's highly unlikely that Mvc would be added "later on", but the argument is imho valid for scenarios with other packages.
I don't know what's a sound/common practice in these cases, but I consider keeping the direct reference as a valid option, if not "a better" option compared to relying on a transitive dependency. Because when the direct dep to .Mvc (in my case) is removed, the direct dep to .Localization is there to "save the day".
But I don't have a strong preference for one or the other way, so I'm ok with removing the direct dep to .Localization
That said, I can see why it would be confusing (it's new behavior).
Thanks for pointing it out. However, not exactly my case, because I'm a casual user of nuget and therefore I have muscle memory of how nuget "should" behave.
If for example another package brought in a higher version of a package that had an issue, or you did not want to use that version, you could reference a lower version directly and get that version instead.
Yes, I completely agree, that this is a neccessary feature. But what I understood from the documentation, I worked out that such cases can be handled by inclusive/exlusive version range, e.g.
"Microsoft.Extensions.Localization": "(,1.1.1]"
All in all, my point is:
It's not the same as an adding an upper bound. Upper bounds are there to make things fail, not change dependency resolution. "Nearest wins" (which honestly is a bad name) really means ancestor wins regardless of what any descendent asked for.
I'm constantly getting errors like
error NU1605: Detected package downgrade: System.Net.Primitives from 4.3.0 to 4.0.11. Reference the package directly from the project to select a different version.
error NU1605: MyProject (>= 1.0.0) -> SomeDependency (=> 1.2.3) -> System.IO.Pipes (>= 4.0.0) -> System.Net.Sockets (>= 4.1.0) -> runtime.unix.System.Net.Sockets (>= 4.3.0) -> System.Net.Primitives (>= 4.3.0)
error NU1605: MyProject (>= 1.0.0) -> SomeDependency (=> 1.2.3) -> System.IO.Pipes (>= 4.0.0) -> System.Net.Primitives (>= 4.0.11)
It means that System.IO.Pipes (4.0.0)
is satisfied with System.Net.Primitives (>= 4.0.11)
, but requires runtime.unix.System.Net.Sockets (>= 4.3.0)
which in turn wants System.Net.Primitives (>= 4.3.0)
.
Dependency graph could be resolved by using System.Net.Primitives = 4.3.0
, but for some reason nuget decided that it should use System.Net.Primitives = 4.0.11
.
And that happens all the time with dozens of packages, even ones from Microsoft .
I don't see any logical reason for doing that. All other package management systems just select a package version that satisfies all requirements, why NuGet is different?
Can we have explicit control over downgrades? Something like
<PackageReference Include="SomeDependency" Version="3.2.1"
ShouldDowngradeChildDependencies="True"/>
That will resolve cases when downgrade is actually desired and won't cause issues otherwise,
@kekekeks you can do this as of VS 15.3
<PackageReference Include="SomeDependency" Version="3.2.1" NoWarn="NU1605"/>
See: https://blog.nuget.org/20170815/Whats-nu-in-NuGet-with-VS2017-15-3.html#msbuild-integration-of-nuget-warnings-and-errors
Here is more explanation on how the resolver selects package versions: https://docs.microsoft.com/en-us/nuget/consume-packages/dependency-resolution
You can override this by referencing the package top level in your project with the version you want to use.
NoWarn removes the warning, but doesn't prevent the downgrade. That's the opposite of what I need. I don't want downgrades to ever happen in my projects, I want NuGet to grab package version that satisfies package dependencies, not some arbitrary nuget policy.
Basically I want a way to disable nearest-wins
policy completely and have satisfy-all-dependencies
policy instead.
I just disagree with this completely. Giving control over the algorithm I think is a non starter. Explicitly describing a top level dependency is a statement of intent, if you don't want the downgrade, don't reference the package directly.
@davidfowl what is the rationale behind not satisfying the dependency and resolving a downgrade in the following situation: (-> represents depends on)
I do not own packages A through D.
In this case, I am not directly referencing Package D at all, and Package D @ Version 1.2.0 would resolve all dependencies without a package downgrade.
I understand the rationale behind allowing a top-level dependency to force a downgrade, but forcing the user to resolve an already resolvable downgrade from exclusively transitive dependencies just seems odd, counterintuitive, and not useful.
@jkoritzinsky
The graph you drew doesn't force a downgrade. That's a common misconception because of the name "nearest" wins. It's not nearest, it's really about direct dependencies of your ancestors.
In the graph you drew above, D 1.2.0 wins. After the "nearest wins pass", is applied, then a pass over the cousin dependencies (what you drew) is applied. In that pass, highest wins.
Here's a simplified way to think about it, you won't get a transitive downgrade warning unless one of your dependencies ignored their downgrade warning and built the package.
Here's an example of what a transitive downgrade happens:
In this case, PackageA is downgrading PackageE's version of D to 1.1.0.
Hope that helps clarify things.
@davidfowl
Just to clarify: a package downgrade warning will only happen if a subtree of the package dependencies has a downgrade (so it's "nearest wins" within a line of decendants if we model the project as the root ancestor and warning if winning is a downgrade)?
@jkoritzinsky yes.
@davidfowl For me this nearest-wins
approach is really annoying because of how we work.
We have package 1 that contains some code in it's own repository with it's own build
We have package 2 that contains some other code and depends on package 1. This also has it's own repository and it's own build.
Then we have projects that would reference package 2.
Now as a developer, i need to extend package 1 with something useful.
Right now, that means that i have to rebuild and publish package 1, then rebuild and publish package 2 for no reason other than to increment dependency version, and then update package 2 in my project.
I could, of course, go and update package 1 in project. However, when another team member updates package 2 to reference package 1 higher than what is in the project, i will get the NU1605
.
This is quite annoying.
I expect to be able to say ^1.0.X
or something in my .csproj
for package 1 so that if package2 has a higher version, the nearest
is automatically upped for minor version updates.
Most helpful comment
I'm constantly getting errors like
It means that
System.IO.Pipes (4.0.0)
is satisfied withSystem.Net.Primitives (>= 4.0.11)
, but requiresruntime.unix.System.Net.Sockets (>= 4.3.0)
which in turn wantsSystem.Net.Primitives (>= 4.3.0)
.Dependency graph could be resolved by using
System.Net.Primitives = 4.3.0
, but for some reason nuget decided that it should useSystem.Net.Primitives = 4.0.11
.And that happens all the time with dozens of packages, even ones from Microsoft .
I don't see any logical reason for doing that. All other package management systems just select a package version that satisfies all requirements, why NuGet is different?