Central Package Management in .NET
Central Package Management - Konsistenz und Übersicht für moderne .NET Solutions.
Banana Cake Pop, Hot Chocolate und Strawberry Shake gehören auf jedes Menü eines Hipster-Cafés, sind aber tatsächlich eine komplette GraphQL-Toolchain für .NET.
Gewusst, nicht gewusst? Egal: Die Welt der Softwareentwicklung ist so riesig, dass man nicht alles kennen kann.
Ein .NET-Feature, das es geschafft hat, sich an mir vorbei zu schleichen, war das Central Package Management (CPM). Das gibt es bereits seit .NET 6 / NuGet 6.2 und gefühlt kennt es kaum jemand.
Was ist CPM?
Anstatt die Versionen unserer Abhängigkeiten direkt in jeder .csproj zu pflegen, legen wir sie zentral in einer Datei im Repository-Root ab:
1
2
3
4
5
6
7
8
9
<!-- Directory.Packages.props -->
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<Project>
<ItemGroup>
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>
In den Projektdateien selbst steht dann nur noch die Referenz:
1
2
3
4
5
6
7
8
9
<!-- Project1.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>
</Project>
Manuelles Umstellen zu CPM
Microsoft empfiehlt:
Schritt 1: dotnet new packagesprops
ausführen um eine Directory.Packages.props zu erzeugen.
Schritt 2: Alle Versionen aus allen Projekten in diese Datei übertragen.
Für mich klingt das schon bei einem einzelnen Projekt anstrengend. Glücklicherweise gibt es den Centralised Package Converter.
Centralised Package Converter
Damit können wir unsere gesamte Solution automatisiert umstellen:
Schritt 1: dotnet tool install CentralisedPackageConverter --global
Schritt 2: central-pkg-converter .
*
1
2
3
4
WARNING: You are about to make changes to the following project files:
Project1.csproj
Project2.csproj
Are you sure you want to continue? [y/n]
Schritt 3: y
🚀
* Ich befinde mich im Terminal meiner IDE meist im Root des Repositories. Alternativ kann man den Pfad angeben.
Warum überhaupt CPM?
NuGet-Pakete quer über eine Solution zu pflegen ist fehleranfällig. Jede .csproj hat ihre eigenen Versionen und irgendwann driften sie auseinander.
Mit CPM erhalten wir:
- Konsistenz in der ganzen Solution
- weniger Merge-Konflikte
- einfachere Updates
- bessere Übersicht aller Abhängigkeiten
Kurz: Weniger Arbeit, weniger Fehler, mehr Ruhe. 🧘
Das waren nur die Basics. Das Überschreiben von Versionen, transitive pinning und einiges mehr lässt sich bei Bedarf einstellen. RTFM. 🤓
Bonus: Docker + CPM
Wer frisch mit CPM anfängt und sich freut, dass es beim lokalen Build läuft, den könnte spätestens während des docker build
ein kleiner Stolperstein überraschen.
nuget restore
versucht die niedrigsten Versionen zu laden, die es gibt. Das liegt daran, dass die Version in der .csproj. optional ist und im Standard die niedrigste Version gezogen wird.
Die Lösung: In der Dockerfile, vor dem Kopieren der Projekte, die Directory.Packages.props kopieren.
COPY Directory.Packages.props ./