NuGet dependency resolution limits: nearest wins behavior
0 reputation · 08 Jun 2021, 04:28 UTC
0 reputation · 08 Jun 2021, 04:28 UTC
When restoring a .NET project, NuGet resolves dependencies by selecting the nearest version in the dependency graph. This “nearest wins” strategy can cause subtle version conflicts, especially when transitive dependencies diverge.
Project assets are recorded in the lock file (project.assets.json) to enforce reproducibility. However, the interaction between the nearest wins rule and lock file locking is not fully documented, leaving teams uncertain about version consistency across a solution.
Specifically, the goal is to understand whether the lock file can override the nearest wins decision when a top‑level package requires a higher version of a shared library, and under what circumstances this override applies.
Questions:
project.assets.json or solution-level packages.lock.json) pins the exact versions resolved during a successful restore. It does not re-run nearest-wins on subsequent restores. All projects that share the same lock file (or have compatible lock files) will use identical versions, even if a fresh nearest-wins calculation would differ.PackageReference with a higher version overrides nearest-wins because it sits at depth zero — the shortest possible path. This is not a special exception; it is the nearest-wins rule applied to the direct reference.NuGet builds a dependency graph per project. When the same package ID is reachable via multiple paths, the algorithm measures the distance (number of edges) from the root project to each occurrence. The version at the minimal distance wins. If two paths have equal depth, the tie-break is deterministic but undocumented (typically the first encountered).
This rule applies during the initial restore. Once a lock file exists, the resolver honors the locked versions and skips the graph walk for those packages.
project.assets.json) are independent. Restoring each project separately can yield different versions for the same transitive package.packages.lock.json) is enabled via nuget.config (config > lockFilePath) or Directory.Build.props (RestorePackagesWithLockFile=true). When active, all projects in the solution read the same locked graph, guaranteeing version consistency.NU1507 when projects in a solution have mismatched lock files, but does not auto-reconcile.PackageReference in the project file is at depth zero, so it always wins over any transitive occurrence.Directory.Packages.props) places every PackageReference at the solution level, making all references depth-zero. Nearest-wins effectively disappears; conflicts become explicit version mismatches that must be resolved in the central file.DependencyVersion settings in nuget.config (Lowest, Highest, HighestMinor, HighestPatch) affect the initial resolution before locking. Once locked, they have no effect until the lock file is cleared.dotnet new sln -n NearestWinsDemo
mkdir ProjectA ProjectB
cd ProjectA && dotnet new classlib
cd ../ProjectB && dotnet new classlib
cd ..
dotnet add ProjectA/ProjectA.csproj package Newtonsoft.Json --version 12.0.3
dotnet add ProjectB/ProjectB.csproj package Newtonsoft.Json --version 13.0.1
dotnet add ProjectB/ProjectB.csproj reference ProjectA/ProjectA.csproj
dotnet restore
cat ProjectA/obj/project.assets.json | grep -A2 -B2 'Newtonsoft.Json'
cat ProjectB/obj/project.assets.json | grep -A2 -B2 'Newtonsoft.Json'
Observe which version each project resolves (nearest-wins picks 12.0.3 for ProjectA, 13.0.1 for ProjectB).dotnet new nugetconfig
# Edit nuget.config: add <config><add key="lockFilePath" value="packages.lock.json" /></config>
dotnet restore --force
cat packages.lock.json | grep -A2 -B2 'Newtonsoft.Json'
Both projects now read the same locked version.dotnet add ProjectA/ProjectA.csproj package Newtonsoft.Json --version 14.0.0
rm -f ProjectA/obj/project.assets.json ProjectB/obj/project.assets.json packages.lock.json
dotnet restore
ProjectA now resolves 14.0.0 (depth-zero wins).packages.config uses a different resolver.1.*) are resolved once and then locked; the locked concrete version participates in nearest-wins like any other pinned version.Are you using a solution-level packages.lock.json (enabled via nuget.config or Directory.Build.props) or relying on per-project project.assets.json files? The answer determines whether cross-project version consistency is guaranteed.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.