Nuget Dependencies and latest Versions
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:
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.
Other Posts you might also like
The Voices of Reason
# re: Nuget Dependencies and latest Versions
If you're interested, check out the "Enter ripple restore for cascading builds" section of http://jeremydmiller.com/2014/04/18/fubumvc-build-management/
# re: Nuget Dependencies and latest Versions
# re: Nuget Dependencies and latest Versions
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.
# re: Nuget Dependencies and latest Versions
# re: Nuget Dependencies and latest Versions
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.
# re: Nuget Dependencies and latest Versions
-DependencyVersion Lowest
to
-DependencyVersion HighestMinor
or
-DependencyVersion Highest
# re: Nuget Dependencies and latest Versions
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
# re: Nuget Dependencies and latest Versions
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.
# re: Nuget Dependencies and latest Versions
# re: Nuget Dependencies and latest Versions
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.
# 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.
# re: Nuget Dependencies and latest Versions
//dm