How NuGet resolves package dependencies

In this article

Any time a package is installed or reinstalled, which includes being installed as part of a restore process, NuGet also installs any additional packages on which that first package depends.

Those immediate dependencies might then also have dependencies on their own, which can continue to an arbitrary depth. This produces what's called a dependency graph that describes the relationships between packages at all levels.

When multiple packages have the same dependency, then the same package ID can appear in the graph multiple times, potentially with different version constraints. However, only one version of a given package can be used in a project, so NuGet must choose which version is used. The exact process depends on the package reference format being used.

Dependency resolution with PackageReference

When installing packages into projects using the PackageReference format, NuGet adds references to a flat package graph in the appropriate file and resolves conflicts ahead of time. This process is referred to as transitive restore. Reinstalling or restoring packages is then a process of downloading the packages listed in the graph, resulting in faster and more predictable builds. You can also take advantage of wildcard (floating) versions, such as 2.8.*, avoiding expensive and error prone calls to nuget update on the client machines and build servers.

When the NuGet restore process runs prior to a build, it resolves dependencies first in memory, then writes the resulting graph to a file called project.assets.json in the obj folder of a project using PackageReference. MSBuild then reads this file and translates it into a set of folders where potential references can be found, and then adds them to the project tree in memory.

The lock file is temporary and should not be added to source control. It's listed by default in both .gitignore and .tfignore. See Packages and source control.

Dependency resolution rules

Lowest applicable version

The lowest applicable version rule restores the lowest possible version of a package as defined by its dependencies. It also applies to dependencies on the application or the class library unless declared as floating.

In the following figure, for example, 1.0-beta is considered lower than 1.0 so NuGet chooses the 1.0 version:

In the next figure, version 2.1 is not available on the feed but because the version constraint is >= 2.1 NuGet picks the next lowest version it can find, in this case 2.2:

When an application specifies an exact version number, such as 1.2, that is not available on the feed, NuGet fails with an error when attempting to install or restore the package:

Floating (wildcard) versions

A floating or wildcard dependency version is specified with the * wildcard, as with 6.0.*. This version specification says "use the latest 6.0.x version"; 4.* means "use the latest 4.x version." Using a wildcard allows a dependency package to continue evolving without requiring a change to the consuming application (or package).

When using a wildcard, NuGet resolves the highest version of a package that matches the version pattern, for example 6.0.* gets the highest version of a package that starts with 6.0:

Note

For information on the behavior of wildcards and pre-release versions, see Package versioning.

Nearest wins

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:

Warning

The Nearest Wins rule can result in a downgrade of the package version, thus potentially breaking other dependencies in the graph. Hence this rule is applied with a warning to alert the user.

This rule also results in greater efficiency with a large dependency graph (such as those with the BCL packages) because once a given dependency is ignored, NuGet also ignores all remaining dependencies on that branch of the graph. In the diagram below, for example, because Package C 2.0 is used, NuGet ignores any branches in the graph that refer to an older version of Package C:

Cousin dependencies

When different package versions are referred to at the same distance in the graph from the application, NuGet uses the lowest version that satisfies all version requirements (as with the lowest applicable version and floating versions rules). In the image below, for example, version 2.0 of Package B satisfies the other >=1.0 constraint, and is thus used:

In some cases, it's not possible to meet all version requirements. As shown below, if Package A requires exactly Package B 1.0 and Package C requires Package B >=2.0, then NuGet cannot resolve the dependencies and gives an error.

In these situations, the top-level consumer (the application or package) should add its own direct dependency on Package B so that the Nearest Wins rule applies.

Dependency resolution with packages.config

With packages.config, a project's dependencies are written to packages.config as a flat list. Any dependencies of those packages are also written in the same list. When packages are installed, NuGet might also modify the .csproj file, app.config, web.config, and other individual files.

With packages.config, NuGet attempts to resolve dependency conflicts during the installation of each individual package. That is, if Package A is being installed and depends on Package B, and Package B is already listed in packages.config as a dependency of something else, NuGet compares the versions of Package B being requested and attempts to find a version that satisfies all version constraints. Specifically, NuGet selects the lower major.minor version that satisfies dependencies.

By default, NuGet 2.8 looks for the lowest patch version (see NuGet 2.8 release notes). You can control this setting through the DependencyVersion attribute in Nuget.Config and the -DependencyVersion switch on the command line.

The packages.config process for resolving dependencies gets complicated for larger dependency graphs. Each new package installation requires a traversal of the whole graph and raises the chance for version conflicts. When a conflict occurs, installation is stopped, leaving the project in an indeterminate state, especially with potential modifications to the project file itself. This is not an issue when using other package reference formats.

Managing dependency assets

When using the PackageReference format, you can control which assets from dependencies flow into the top-level project. For details, see PackageReference.

When the top-level project is itself a package, you also have control over this flow by using the include and exclude attributes with dependencies listed in the .nuspec file. See .nuspec Reference - Dependencies.

Excluding references

There are scenarios in which assemblies with the same name might be referenced more than once in a project, producing design-time and build-time errors. Consider a project that contains a custom version of C.dll, and references Package C that also contains C.dll. At the same time, the project also depends on Package B which also depends on Package C and C.dll. As a result, NuGet can't determine which C.dll to use, but you can't just remove the project's dependency on Package C because Package B also depends on it.

To resolve this, you must directly reference the C.dll you want (or use another package that references the right one), and then add a dependency on Package C that excludes all its assets. This is done as follows depending on the package reference format in use:

packages.config: remove the reference to PackageC from the .csproj file so that it references only the version of C.dll that you want.

Dependency updates during package install

If a dependency version is already satisfied, the dependency isn't updated during other package installations. For example, consider package A that depends on package B and specifies 1.0 for the version number. The source repository contains versions 1.0, 1.1, and 1.2 of package B. If A is installed in a project that already contains B version 1.0, then B 1.0 remains in use because it satisfies the version constraint. However, if package A had requests version 1.1 or higher of B, then B 1.2 would be installed.

Resolving incompatible package errors

During a package restore operation, you may see the error "One or more packages are not compatible..." or that a package "is not compatible" with the project's target framework.

This error occurs when one or more of the packages referenced in your project do not indicate that they support the project's target framework; that is, the package does not contain a suitable DLL in its lib folder for a target framework that is compatible with the project. (See Target frameworks for a list.)

For example, if a project targets netstandard1.6 and you attempt to install a package that contains DLLs in only the lib\net20 and \lib\net45 folders, then you see messages like the following for the package and possibly its dependents:

Retarget your project to a framework that is supported by the packages you want to use.

Contact the author of the packages and work with them to add support for your chosen framework. Each package listing page on nuget.org has a Contact Owners link for this purpose.

Note

The feedback system for this content will be changing soon. Old comments will not be carried over. If content within a comment thread is important to you, please save a copy. For more information on the upcoming change, we invite you to read our blog post.