NuGet and dependency management
Version ranges, transitive dependencies, central package management and a restore you can reproduce on any machine.
Versioning and how resolution works
| Notation | Meaning | Assessment |
|---|---|---|
8.0.5 | Minimum of 8.0.5, and NuGet may select higher | The normal pin; combined with a lock file it is exact |
[8.0.5] | Exactly 8.0.5 | Strictest, rarely needed |
[8.0,9.0) | A range | Common in libraries, dangerous in applications |
8.* | Floating, lowest matching | Avoid; builds stop being reproducible |
8.0.5-* | Floating prerelease | CI experiments only |
[8.0.5,) | Minimum, unrestricted upward | An accidental major upgrade waiting to happen |
NuGet resolves the union of every requirement in the graph and picks the lowest version that satisfies them all. A direct reference to a lower version than a transitive dependency requires does not downgrade it, so the effective version is not always the one you wrote.
# See what was actually resolved, including transitives
dotnet list package --include-transitive
# See what a package is pulling in and why
dotnet nuget why MyApp.csproj Microsoft.Extensions.Logging
# Disable a floating version before it surprises you
dotnet list package --outdated --include-transitive
# Vulnerability audit, which is also part of restore in recent SDKs
dotnet list package --vulnerable --include-transitiveCentral package management
<!-- Directory.Packages.props at the repository root -->
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.1" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
</ItemGroup>
</Project><!-- In each project file, no Version attribute: one place to change it -->
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="xunit" />
</ItemGroup># The lock file pins the full graph; commit it and restore from it in CI
dotnet restore --locked-mode
# After an intentional upgrade, regenerate the lock files
dotnet restore --force-evaluate
git add "**/packages.lock.json"- Central package management removes version drift between projects in a repository, which is the usual source of a mysterious transitive conflict.
- Transitive pinning lets you promote a transitive dependency to an explicit version in the one central file, which is how you force a patched version of something you do not reference directly.
- A lock file makes restore deterministic. Without it, a floating or minimum-version reference can resolve differently on a developer machine and in CI.
- Restore sources come from nuget.config. Mirror the public feed in an air-gapped or rate-limited environment and disable sources you do not intend to use.
- A private feed with credentials keeps secrets out of source control: reference an environment variable rather than embedding a key.
- Audit on every build. A vulnerability fix is usually a patch-version bump in the central file, and it is far cheaper when the change is one line.
⚠️
Never resolve a diamond conflict by editing the generated project.assets.json or by adding a binding redirect by hand. Change the direct reference or use transitive pinning so the resolution is expressed in source and reproducible everywhere.
FAQ
Should I commit packages.lock.json?
Yes for applications, and for libraries it is your choice — a library consumer gets their own resolution. For anything you deploy, commit it and restore with --locked-mode in CI.
Why is the resolved version higher than what I specified?
Because another package in the graph requires at least that version. NuGet takes the lowest version satisfying all constraints, which can be higher than your direct reference.
Related
Writing and publishing NuGet libraries Project types, target frameworks and multi-targeting
Last refreshed 2026-09-18.