Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Nuget Dependencies and latest Versions


:P
On this page:

NuGet is one of the best things that happened to .NET, to make it easier to share and distributed shared components. NuGet has become Microsoft’s main inclusion mechanism (and it looks to be come even more prominent in ASPvNext which eschews binary deployments for NuGet packages). And there are a bazillion third party components you can now get through NuGet. While it’s not all unicorns and rainbows, NuGet has been one of the nicest additions to .NET to move the platform forward.

Personally I also use NuGet to distribute a host of Westwind libraries as part of the Westwind.Toolkit. I update those libraries frequently and it’s made it super easy to get new versions out and distributed to existing users, as well for my own work. Whether internal or external it’s a nice way to push components into applications.

Publishing and Dependency Management

However, when it comes to publishing NuGet packages I’ve had more than a little trouble getting my dependencies to come in with the proper version. Basically when you create a component that has a dependency on another component you can specify a version number or number range that you are depending on.

For example, here is the Westwind.Data nuget which depends on Westwind.Utilities and depends on version 2.50 and later:

<dependencies>
    <dependency id="Westwind.Utilities" version="[2.50,3.0)" />
    <dependency id="EntityFramework" version="6.1.0" />
</dependencies>

This basically says to depend on version 2.50 or higher up to version 2.99 (anything under 3.0) of Westwind.Utilities.

Now when I read the rather cryptic page that describes the dependency version management here:

My interpretation of the expression above based on that documentation would be – load version the highest version as long as it’s lower than 3.0. But that’s not actually what happens. In fact, quite the opposite occurs: Rather than loading the latest version, the smallest possible version is used, so even though the current version on NuGet for Westwind.Utilities is at 2.55, if you add this NuGet it’ll load version 2.50.

Here’s an article that describes NuGet’s default behavior (thanks to @BrianDukes):

It would be really helpful if the behavior described in the second article was also provided in the first. Duh! ‘Cause you know, that’s kind of crucial information that’s not freaking obvious!

To demonstrate: This gets much worse if you use the following syntax (which I accidentally used at first):

<dependency id="Westwind.Utilities" version="[,3.0)" />

which looks like it would mean to load any version lower than 3.0. But in fact it’ll load the lowest version which is 1.27, which is a totally incompatible version (2.0 had breaking changes). So at the very least it’s always best to include a lower boundary version, but even if I specifed:

<dependency id="Westwind.Utilities" version="[2.0,3.0)" />

it would work, but this would always load 2.0 unless a higher version already existed in the project.

I’m not sure why anybody would ever use this option when you explicitly provide high and low version constraints? When would you ever want to load an old component knowing that it would always load the old one? This seems rather pointless.

It seems the only option I really have here to get the latest version to load is to explicitly provide the latest version that is current and provide in the lower version explicitly and match it to the current version:

<dependency id="Westwind.Utilities" version="[2.55,3.0)" />

That way at least I get the latest version that was available at the time the component was created.

But the downside to that is that older versions that might already be in the project would not be allowed and you’d end up with a version conflict unless all components are upgraded to this latest version.

You can see how this gets ugly really quick. This is not all NuGet’s fault BTW – this is really a versioning issue, but it hits when the components are installed and requested in the first place and the point of entry where the pain occurs happens to be NuGet. The issue is really component dependency versioning and runtime binding, where a single application might have multiple dependencies on the same assembly with different versions. There are no easy answers here and I don’t want to get into this argument because that’s an endless discussion – and hopefully this will be addressed much better in the new ASPvNext stack that seems to allow for side by side execution of the same assemblies (via Roslyn magic perhaps)?

Lowest Common Denominator and Overriding

NuGet by default loads the lowest possible version it can match. In this case that’s 2.50, even though 2.55 is available. There’s a bit of discussion on why this somewhat unintuitive behavior occurs. Summarized, it amounts to this: lower versions are safer and avoid pulling in newer versions of components that might break existing applications/components that depend on the same component. It avoids pulling in newer versions in unconditionally.

This behavior can be explicitly overridden by explicitly running a component install with an extra switch:

http://blog.myget.org/post/2014/05/05/Picking-the-right-dependency-version-adding-packages-from-NuGet.aspx

which amounts to this:

PM> install-package westwind.utilities  -DependencyVersion Highest

That’s nice, but as a package author that still leaves you unable to force the latest version even if you explicitly delimit your version range as I’ve done above *and* you want to support older versions as well for backwards compatible loading.

NuGet should be smarter than that!

I can see the argument of making sure projects are not broken by components automatically revving to a new higher version.

But it seems to me that NuGet should be smart enough to detect if you’re installing a component with a dependency for the first time when you have an upper version constraint and in that case it should install the LATEST version allowed of that component. There’s no point to add a new component with an old version UNLESS there’s a conflict with an existing package that is already installed in the project.

As it stands today, the version range features don’t really behave the way I would expect them to work. The ranges don’t really apply a range of versions to install, but rather act as a restraint to ensure you’re not stepping on an existing component by checking that an already installed component is in the version range allowed. But as for installation, the default to the lowest version pretty much ensures that NuGet always installs the lowest version, which is pretty lame if you think about it.

If the component is already installed and it’s in the range that’s valid then I can understand leaving the existing component alone and that would be the correct behavior in order to not accidentally screw up dependencies. We can then manually do the

update-package westwind.utilities

to get the component to the latest version in that scenario.

But in the case where the dependency is loaded for the very first time, it makes no sense to load the oldest version… Discuss.

Posted in .NET  

The Voices of Reason


 

Dennis
June 20, 2014

# re: Nuget Dependencies and latest Versions

I think they did the lowest version number, based on that you will mark the the older versions as deleted/removed on the feeds, this way you choose what the lowest version is publically available for install, but restores with older versions still Work.

//dm

Steven Maglio
June 20, 2014

# re: Nuget Dependencies and latest Versions

It looks like the FubuMVC project also ran into issues with the way NuGet versioning resolves. They attempted to work around the problem by building a product called Ripple. I don't believe they were focused on the use case where there's an upper limiter on the version number, so I don't know if they added that feature. But, I believe the project is still under active development; so it could probably be added.

If you're interested, check out the "Enter ripple restore for cascading builds" section of http://jeremydmiller.com/2014/04/18/fubumvc-build-management/

Sean
June 21, 2014

# re: Nuget Dependencies and latest Versions

Agree with all of this. NuGet is fast becoming the new Dll-Hell.

Andrew Miller
June 23, 2014

# re: Nuget Dependencies and latest Versions

I've been struggling with this same issue, and finally came across info about why they made this change to NuGet, in the v2.8 release notes:
http://docs.nuget.org/docs/release-notes/nuget-2.8

I agree it's a pain in our situation, but I understand why they made the change.

Pete
July 23, 2014

# re: Nuget Dependencies and latest Versions

Currently experiencing pain because the SignalR package and it's dependencies (specifically OWIN) don't seem to be setup correctly (we had a similar problem a while ago with ODATA). Very very annoying!

John Washburn
November 25, 2014

# re: Nuget Dependencies and latest Versions

Microsoft and NuGet are so close to providing the same functionality as the CPAN had in 1998. I hope nuget gets to that level of sophistication soon.

What is the point of requiring nuget author to use semantic versioning and then NOT let other nuget package author *rely* on that that semantic versioning.

http://docs.nuget.org/docs/reference/versioning

States:
In brief, these correspond to: * Major: Breaking changes. * Minor: New features, but backwards compatible. * Patch: Backwards compatible bug fixes only.

I agree that if I specify [2.0.0, 3.0.0) then I would think that I am spcifying that the latest non breaking version of 2.x of the nuget package is acceptable to my nuget package.

Requiring the user to specify
-DependencyVersion Highest
is inane.

At the least I should be specify in the nuspec that lowest or highest is preferred and then user can override it on the command line and at their own risk.

John Washburn
November 25, 2014

# re: Nuget Dependencies and latest Versions

Is there an options setting to change default from
-DependencyVersion Lowest
to
-DependencyVersion HighestMinor
or
-DependencyVersion Highest

Eric Newton
November 16, 2015

# re: Nuget Dependencies and latest Versions

"Agree with all of this. NuGet is fast becoming the new Dll-Hell."

You are SOOOOOOO right. Microsoft has reintroduced DLL VERSION HELL from the COM interface days. This is because the framework itself does the exact opposite of most of the more modern languages (javascript) that utilizes "whats present" for the assembly.

The whole version binding scheme in .NET has been broken all along, because it shouldn't be binding directly to a version (1.1.2.3) but should bind to "whatever assembly is present that presents 1.1"

Case in point: having an ever-growing list of "runtime binding redirects" present in web.config for ASPNET and MVC bits. .Net should AUTOMATICALLY bind to the major/minor present in the bin directory irregardless of whether its MVC 4.0.0.1 or MVC 4.0.3.555

Jeff Fritz
December 21, 2015

# re: Nuget Dependencies and latest Versions

In 2.7-2.9 versions of NuGet, you're right... there are some strange things that you can't control about the updates. There are configuration options that you can set in nuget.config to force the dependency version selection, check the nuget.config options at: http://docs.nuget.org/consume/NuGet-Config-Settings

In NuGet v3 with project.json, we've introduced the ability to specify only the top-level dependencies that your project requires and the NuGet client will determine which versions of dependency packages to download and install for your project. Additionally, all packages are stored in a central cache on your workstation so that they are not downloaded repeated times. Finally, we have added all of these configuration options for determining which versions to install inside of the NuGet UI in Visual Studio.

We heard these concerns from developers like Rick and took steps with NuGet v3 over the last year to give MORE control over package installation and maintenance in your projects.

Rick Strahl
December 21, 2015

# re: Nuget Dependencies and latest Versions

@Jeff - I also like the new switch in VS 2015 which lets you choose whether to load the 'safe' version or the latest version which is a nice improvement - at least you get an easy way to control the behavior I complain about above. In the end I just opted to use specific version references which seem to work as expected (ie. version="2.54.0") in grabbing the matching version or higher version if the flag is set. I haven't tried it yet, but I assume NuGet respects sematic versioning and won't jump past the major version when you allow latest version?

Bert Cotton
April 04, 2016

# re: Nuget Dependencies and latest Versions

Following up what Rick said, by targeting a specific version and then having NuGet resolve a difference version is very concerning. It removes the ability to guarantee reproducible builds. Building today and targeting 2.54.0 may not have the same result as building tomorrow and targeting 2.54.0, if NuGet pulled in 2.54.1.

This seems backwards from other package management solutions, such as Maven and NodeJS.

It would be helpful if in my project.json I could specify this behavior. Maybe a strict version keyword or something.

Brennon Williams
July 22, 2017

# re: Nuget Dependencies and latest Versions

@Rick - Thanks for taking the time to write so many great posts. Your knowledge is really appreciated. This post explained a lot of issues I see and have.

Unfortunately many issues still persist in NuGet 3 in terms of versioning. I understand the difficulties and complexity of large scale version management, so I need to breath in and not unleash on any one person or group in particular.

Take for example JSON.net which I think most could agree is a fantastic library. I am thankful for the skill that has gone into producing it.

But consistently I find almost all major issues in my builds are rooted in some instance to NuGet package installation of it. Particularly nasty, when these issues raise in Release builds running on bare bone VM's with no additional tooling installed.

Take for example: I have several projects in a large and complex solution. Each one of those projects has v 10.0.2.0 installed from NuGet. I have to put in binding redirects in every app.manifest to force compatibility. So naturally one would assume the redirect should target 10.0.2.0.

But you see, here is the issue. NuGet says 10.0.2.0 is installed, but actually the file version is 10.0.0.0

So everything fails on the redirects as you would expect.

@Jeff I know this a year after this post, but your team needs to tighten up on the terminology usage in the NuGet UI. File versions, Runtime versions, Package versions, File Product versions etc. In the case of this JSON.net file, every single version number is different, so all we can rely on is the NuGet UI. This may also be down to how the package was published by Newtonsoft.

Either way - version inconsistency (and the specific right version object) is the root causes of DLL HELL returning.

One thing that would surely stop a lot of package issues would be to collectively warn the user if and when package versions are different from what is expected, after a compile (as in package manager assembly version comparison to the files).

Too many times we find old versions sitting in OBJ and BIN folders that are not cleared out by solution cleans.

Pick one version object and stick to it as the "master" version to display. Propagate that all the way through your tool chains and UI's (from publish to consumer) and a lot of issues will go away.


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024