Auflösung von Paketabhängigkeiten durch NuGet

Bei jeder Installation oder Neuinstallation eines Pakets, was auch die Installation als Bestandteil eines Wiederherstellungsprozesses umfasst, installiert NuGet außerdem zusätzliche Pakete, von denen das ursprüngliche Paket abhängig ist.

Diese unmittelbaren Abhängigkeiten verfügen möglicherweise ebenfalls über Abhängigkeiten, die über weitere Abhängigkeiten verfügen, usw. Dadurch entsteht ein sogenanntes Abhängigkeitsdiagramm, das die Beziehungen zwischen den Paketen auf sämtlichen Ebenen darstellt.

Wenn mehrere Pakete über dieselbe Abhängigkeit verfügen, kann in dem Diagramm auch mehrmals dieselbe Paket-ID angezeigt werden, möglicherweise mit verschiedenen Versionseinschränkungen. Trotzdem kann nur eine Version eines vorhandenen Pakets in einem Projekt verwendet werden, sodass NuGet auswählen muss, welche Version verwendet wird. Der genaue Prozess ist abhängig vom verwendeten Paketverwaltungsformat.

Abhängigkeitsauflösung mit PackageReference

Bei der Installation von Paketen in Projekte, die das PackageReference-Format verwenden, fügt NuGet Verweise auf ein flaches Paketdiagramm in der entsprechenden Datei hinzu und löst Konflikte auf, bevor sie entstehen. Dieser Vorgang wird als transitive Wiederherstellung bezeichnet. Für die erneute Installation oder die Wiederherstellung müssen dann nur noch die in dem Diagramm aufgeführten Pakete heruntergeladen werden, wodurch schnellere und zuverlässigere Builds erstellt werden.

Sie können auch die Vorteile unverankerter Versionen nutzen, beispielsweise 2.8.*, damit das Projekt nicht geändert werden muss, um die neueste Version eines Pakets zu verwenden. Bei Verwendung unverankerter Versionen wird empfohlen, die Sperrdateifunktionalität zu aktivieren, um die Wiederholbarkeit sicherzustellen.

Wenn der Wiederherstellungsprozess von NuGet vor einem Buildvorgang ausgeführt wird, löst dieser zuerst Abhängigkeiten im Arbeitsspeicher aus und schreibt das resultierende Diagramm dann in eine Datei mit der Bezeichnung project.assets.json.

Die Ressourcendatei befindet sich unter MSBuildProjectExtensionsPath, was standardmäßig der Objektordner des Projekts ist. MSBuild liest diese Datei und übersetzt sie in mehrere Ordner, in denen sich mögliche Verweise befinden, und fügt diese dann der Projektstruktur im Arbeitsspeicher hinzu.

Die project.assets.json-Datei dient nur als temporäre Datei und sollte nicht der Quellcodeverwaltung hinzugefügt werden. Sie wird standardmäßig sowohl in .gitignore als auch in .tfignore aufgeführt. Weitere Informationen finden Sie unter Packages and source control (Pakete und Quellcodeverwaltung).

Regeln für die Abhängigkeitsauflösung

Die transitive Wiederherstellung wendet vier Hauptregeln an, um Abhängigkeiten aufzulösen: niedrigste anwendbare Version, gleitende Versionen, direkte Abhängigkeitsgewinne und Cousin-Abhängigkeiten.

Niedrigste anwendbare Version

Die Regel der niedrigsten anwendbaren Version stellt die niedrigste mögliche Version eines Pakets in der Form wieder her, wie sie von dessen Abhängigkeiten definiert wird. Dies gilt auch für Abhängigkeiten von der Anwendung oder der Klassenbibliothek, wenn sie nicht als unverankert deklariert werden.

In der folgenden Abbildung wird beispielsweise die Betaversion 1.0 niedriger eingeordnet als die Version 1.0, und NuGet entscheidet sich für die Version 1.0:

Choosing the lowest applicable version

In der nächsten Abbildung ist Version 2.1 nicht im Feed verfügbar. Da die Versionseinschränkung jedoch „>= 2.1“ lautet, wählt NuGet die nächstniedrigere Version aus, die es finden kann. Das ist in diesem Fall Version 2.2:

Choosing the next lowest version available on the feed

Wenn eine Anwendung eine genaue Versionsnummer wie 1.2 angibt, die auf dem Feed nicht verfügbar ist, löst NuGet bei dem Versuch, das Paket zu installieren oder wiederherzustellen, einen Fehler aus:

NuGet generates an error when an exact package version is not available

Unverankerte Versionen

Eine unverankerte Abhängigkeitsversion wird mit dem Zeichen * angegeben. Beispiel: 6.0.*. Bei dieser Versionsangabe wird die neueste Version 6.0.x verwendet. 4.* führt dazu, dass die neueste 4.x-Version verwendet wird. Bei Verwendung einer unverankerten Version sind nicht so viele Änderungen an der Projektdatei erforderlich. Gleichzeitig bleiben Sie mit der neuesten Version einer Abhängigkeit auf dem neuesten Stand. Unverankerte Versionen können nur auf Projektebene angegeben werden.

Wenn eine unverankerte Version verwendet wird, löst NuGet die höchste Version eines Pakets auf, die dem Versionsmuster entspricht. Beispielsweise wird für 6.0.* die höchste Version eines Pakets abgerufen, die mit 6.0 beginnt:

Choosing version 6.0.1 when a floating version 6.0.* is requested

Version Auf dem Server vorhandene Versionen Lösung `Reason` Hinweise
* 1.1.0
1.1.1
1.2.0
1.3.0-alpha
1.2.0 Dies ist die höchste stabile Version.
1.1.* 1.1.0
1.1.1
1.1.2-alpha
1.2.0-alpha
1.1.1 Dies ist die höchste stabile Version, die das angegebene Muster einhält.
*-* 1.1.0
1.1.1
1.1.2-alpha
1.3.0-beta
1.3.0-beta Dies ist die höchste Version (einschließlich der nicht stabilen Versionen). Sie ist in Visual Studio-Version 16.6, NuGet-Version 5.6 und .NET Core SDK-Version 3.1.300 verfügbar.
1.1.*-* 1.1.0
1.1.1
1.1.2-alpha
1.1.2-beta
1.3.0-beta
1.1.2-beta Dies ist die höchste Version, die das Muster einhält (einschließlich der nicht stabilen Versionen). Sie ist in Visual Studio-Version 16.6, NuGet-Version 5.6 und .NET Core SDK-Version 3.1.300 verfügbar.

Hinweis

Die Gleitkommaversionsauflösung berücksichtigt nicht, ob ein Paket aufgeführt ist oder nicht. Die Auflösung von unverankerten Versionen wird lokal durchgeführt, wenn die Bedingungen mit Paketen im globalen Paketordner erfüllt werden können.

Direkte Abhängigkeit siegt

Wenn das Paketdiagramm für eine Anwendung unterschiedliche Versionen eines Pakets in demselben Unterdiagramm enthält und eine dieser Versionen eine direkte Abhängigkeit in diesem Unterdiagramm ist, wird diese Version für dieses Unterdiagramm ausgewählt, und der Rest wird ignoriert. Durch dieses Verhalten kann die Anwendung eine bestimmte Paketversion in dem Abhängigkeitsdiagramm außer Kraft setzen.

Im folgenden Beispiel ist die Anwendung direkt von Paket B mit der Versionseinschränkung „>=2.0.0“ abhängig. Außerdem ist die Anwendung von Paket A abhängig, welches wiederum von Paket B abhängig ist. Hierbei gilt jedoch die Einschränkung „>=1.0.0“. Da die Abhängigkeit von Paket B 2.0.0 eine direkte Abhängigkeit von der Anwendung im Diagramm ist, wird diese Version verwendet:

Application using the Direct dependency wins rule

Warnung

Die Regel "Direct Dependency Wins" kann zu einer Herabstufung der Paketversion führen, wodurch andere Abhängigkeiten im Graphen möglicherweise nicht mehr bestehen. Wenn ein Paket herabgestuft wird, fügt NuGet eine Warnung hinzu, um den Benutzer zu benachrichtigen.

Diese Regel führt auch zu einer höheren Effizienz mit einem großen Abhängigkeitsdiagramm. Wenn eine engere Abhängigkeit im selben Unterdiagramm eine höhere Version hat als eine weiter entfernte, ignoriert NuGet diese Abhängigkeit, und NuGet ignoriert auch alle übrigen Abhängigkeiten in diesem Zweig des Diagramm.

Dies wird beispielsweise im folgenden Diagramm deutlich: Das Paket C 2.0.0 wird verwendet, weshalb NuGet jegliche Verzweigungen in dem Diagramm ignoriert, die auf eine ältere Version von Paket C verweisen:

When NuGet ignores a package in the graph, it ignores that entire branch

Durch diese Regel versucht NuGet, die Absicht des Paketautors zu berücksichtigen. Im folgenden Diagramm hat der Autor von Paket A ausdrücklich von Paket C 2.0.0 auf Paket C 1.0.0 heruntergestuft.

When a package author explicitly downgrades, NuGet honors that.

Der Eigentümer der Anwendung kann das Paket C auf eine höhere Version als 2.0.0 aktualisieren, so dass die Version von Paket C nicht weiter herabgestuft wird. In diesem Fall wird keine Warnung ausgegeben.

When an application honor adds a direct dependency for a downgraded package, NuGet honors that.

Verwandte Abhängigkeiten

Wenn verschiedene Paketversionen in verschiedenen Untergraphen im Graphen von der Anwendung referenziert werden, verwendet NuGet die niedrigste Version, die alle Versionsanforderungen erfüllt (wie bei den Regeln für die niedrigste anwendbare Version und die gleitenden Versionen). In der folgenden Abbildung entspricht beispielsweise Version 2.0.0 von Paket B der Einschränkung „>=1.0.0“ und wird deshalb verwendet:

Resolving cousin dependencies using the lower version that satisfies all constraints

Beachten Sie, dass die Pakete nicht auf der gleichen Strecke liegen müssen, damit die Regel der Cousin-Abhängigkeiten gilt. Im folgenden Diagramm wird Paket D 2.0.0 im Teilgraphen von Paket C und Paket D 3.0.0 im Unterdiagramm von Paket A ausgewählt. Im Unterdiagramm der Anwendung gibt es keine direkte Abhängigkeit zu Paket D, daher wird die Regel der niedrigsten anwendbaren Version angewendet und Version 3.0.0 ausgewählt.

Resolving cousin dependencies using the lower version that satisfies all constraints at different distances

Manchmal ist es nicht möglich, allen Versionsanforderungen zu entsprechen. Wenn Paket A, wie in der folgenden Abbildung dargestellt, genau Paket B 1.0.0 erfordert und Paket C die Einschränkung „Paket B >=2.0.0“ erfordert, kann NuGet die Abhängigkeiten nicht auflösen und verursacht einen Fehler.

Unresolvable dependencies due to an exact version requirement

In Fällen wie diesem sollte der Benutzer auf oberster Ebene (die Anwendung oder das Paket) seine eigenen Abhängigkeiten von Paket B hinzufügen, sodass die Regel Nearest Dependency Wins angewendet werden kann.

Abhängigkeitsauflösung mit „packages.config“

Bei packages.config-Dateien werden die Abhängigkeiten eines Projekts als flache Listen in packages.config geschrieben. Jegliche Abhängigkeiten dieser Pakete werden in diese Liste geschrieben. Bei der Installation von Paketen ändert NuGet möglicherweise die .csproj-Datei, app.config, web.config oder andere einzelne Dateien.

Im Hinblick auf die packages.config-Datei versucht NuGet, Abhängigkeitskonflikte aufzulösen, die bei der Installation einzelner Pakete entstehen können. Das bedeutet: Wenn Paket A installiert wird und von Paket B abhängig ist, wobei Paket B bereits in der packages.config-Datei als Abhängigkeit eines anderen Objekts aufgeführt ist, vergleicht NuGet die verlangten Versionen von Paket B und versucht, eine Version zu finden, die allen Versionseinschränkungen entspricht. NuGet wählt also die niedrigste Hauptversion.Nebenversion aus, die allen Abhängigkeiten entspricht.

NuGet 2.8 sucht standardgemäß nach der niedrigsten Patchversion (siehe NuGet 2.8 release notes (Anmerkungen zu NuGet Version 2.8)). Diese Einstellung können Sie über das DependencyVersion-Attribut in der NuGet.Config-Datei und den -DependencyVersion-Schalter in der Befehlszeile ändern.

Der packages.config-Vorgang zum Auflösen von Abhängigkeiten gestaltet sich bei größeren Abhängigkeitsdiagrammen als schwierig. Bei jeder neuen Paketinstallation ist ein Durchlauf des gesamten Diagramms erforderlich, wobei Versionskonflikte entstehen können. Wenn ein Konflikt entsteht, wird die Installation angehalten. Dann befindet sich das Projekt in einem unbestimmten Zustand, insbesondere, wenn Änderungen an der Projektdatei vorgenommen werden. Dieses Problem tritt nicht auf, wenn andere Formate für die Paketverwaltung verwendet werden.

Verwalten von Abhängigkeitsobjekten

Wenn Sie das PackageReference-Format verwenden, können Sie kontrollieren, welche Objekte aus den Abhängigkeiten in die Projekte auf oberster Ebene fließen. Weitere Informationen finden Sie unter PackageReference.

Wenn es sich bei dem Projekt auf oberster Ebene bereits um ein Paket handelt, können Sie diesen Flow kontrollieren, indem Sie die Attribute include und exclude verwenden. Dabei müssen Abhängigkeiten in der .nuspec-Datei aufgeführt sein. Informationen dazu finden Sie unter .nuspec Reference – Dependencies (NUSPEC-Referenz: Abhängigkeiten).

Ausschließen von Verweisen

In einigen Szenarios wird möglicherweise mehrmals in einem Projekt auf Assemblys verwiesen, die denselben Namen haben, wodurch Fehler ausgelöst werden, die die Entwurfszeit und die Buildzeit betreffen. Nehmen wir z.B. ein Projekt mit einer benutzerdefinierten Version von C.dll, das auf Paket C verweist, das ebenfalls C.dll enthält. Gleichzeitig ist das Projekt von Paket B abhängig, das ebenfalls von Paket C und C.dll abhängig ist. Aus diesem Grund kann NuGet nicht bestimmen, welche C.dll-Version es verwenden soll. Sie können dabei aber nicht die Abhängigkeit des Projekts von Paket C entfernen, da auch Paket B davon abhängig ist.

Wenn Sie dieses Problem lösen möchten, müssen Sie direkt auf die Version von C.dll verweisen, die verwendet werden soll, oder ein anderes Paket verwenden, dass auf die richtige Version verweist. Fügen Sie anschließend eine Abhängigkeit von Paket C hinzu, die alle Objekte dieses Pakets ausschließt. Abhängig von dem verwendeten Format für die Paketverwaltung, führen Sie dafür folgende Schritte aus:

  • PackageReference: Fügen Sie der Abhängigkeit ExcludeAssets="All" hinzu:

    <PackageReference Include="PackageC" Version="1.0.0" ExcludeAssets="All" />
    
  • packages.config: Entfernen Sie den Verweis auf Paket C von der .csproj-Datei, sodass nur auf die Version von C.dll verwiesen wird, die Sie verwenden möchten.

Aktualisierung von Abhängigkeiten bei der Paketinstallation

Wenn eine Abhängigkeitsversion bereits erfüllt ist, wird die Abhängigkeit nicht bei anderen Paketinstallationen aktualisiert. Nehmen wir z.B. Paket A, das von Paket B abhängig ist und die Versionsnummer 1.0 angibt. Das Quellrepository enthält die Versionen 1.0, 1.1 und 1.2 von Paket B. Wenn Paket A dann in einem Projekt installiert wird, das bereits Version 1.0 von Paket B enthält, wird Paket B Version 1.0 weiterhin verwendet, weil es die Versionseinschränkungen erfüllt. Wenn allerdings Paket A Version 1.1 oder höher von Paket B verlangt, wird Paket B 1.2 installiert.

Auflösen von inkompatiblen Paketfehlern

Bei der Wiederherstellung eines Pakets wird möglicherweise der Fehler „One or more packages are not compatible.“ („Mindestens ein Paket ist nicht kompatibel.“) angezeigt, oder es wird gemeldet, dass ein Paket „nicht kompatibel“ mit dem Zielframework des Projekts ist.

Dieser Fehler wird ausgelöst, wenn für mindestens eins der Pakete, auf die in dem Projekt verweisen wird, nicht angegeben ist, dass es das Zielframework des Projekts unterstützt. D.h., das Paket enthält keine passende DLL in seinem lib-Ordner für ein mit dem Projekt kompatibles Zielframework. (Eine Liste dazu finden Sie unter Zielframeworks.)

Wenn Sie beispielsweise versuchen, in einem Projekt für netstandard1.6 ein Paket zu installieren, das nur in den Ordnern lib\net20 und \lib\net45 DLLs enthält, werden Meldungen für das Paket und dessen abhängigen Objekte angezeigt, die der Folgenden ähnlich sind:

Restoring packages for myproject.csproj...
Package ContosoUtilities 2.1.2.3 is not compatible with netstandard1.6 (.NETStandard,Version=v1.6). Package ContosoUtilities 2.1.2.3 supports:
  - net20 (.NETFramework,Version=v2.0)
  - net45 (.NETFramework,Version=v4.5)
Package ContosoCore 0.86.0 is not compatible with netstandard1.6 (.NETStandard,Version=v1.6). Package ContosoCore 0.86.0 supports:
  - 11 (11,Version=v0.0)
  - net20 (.NETFramework,Version=v2.0)
  - sl3 (Silverlight,Version=v3.0)
  - sl4 (Silverlight,Version=v4.0)
One or more packages are incompatible with .NETStandard,Version=v1.6.
Package restore failed. Rolling back package changes for 'MyProject'.

Führen Sie die folgenden Schritte aus, um Inkompatibilitäten aufzulösen:

  • Richten Sie Ihr Projekt auf ein anderes Framework aus, das von den Paketen unterstützt wird, die Sie verwenden möchten.
  • Kontaktieren Sie den Autor der Pakete, und arbeiten Sie mit diesem zusammen, um die Unterstützung des ausgewählten Frameworks hinzuzufügen. Dafür verfügt jede Seite mit Auflistungen von Paketen auf nuget.org über den Link Besitzer kontaktieren.

Tipp

Alternative Lösung: NuGetSolver ist eine von Microsoft DevLabs entwickelte Visual Studio-Erweiterung, die bei der Auflösung von Abhängigkeitskonflikten helfen soll. Sie automatisiert den Prozess der Identifizierung und Behebung dieser Probleme. Weitere Details finden Sie auf der NuGetSolver-Seite auf dem Visual Studio Marketplace und wir freuen uns, Ihr Feedback zu Ihrer Erfahrung zu hören.