Auswählen von Assemblys, auf die von Projekten verwiesen wird

Assemblys werden während eines Builds auf zwei verschiedene Arten verwendet. Die erste Möglichkeit besteht in der Verwendung für die Kompilierung, wodurch der Code der Paketbenutzer*innen für APIs in der Assembly kompiliert werden, und für das Bereitstellen von Vorschlägen durch IntelliSense. Die zweite Option ist auf die Laufzeit ausgelegt, zu der die Assembly in das bin-Verzeichnis kopiert und während der Programmausführung verwendet wird. Einige Paketautor*innen möchten, dass ihren Paketbenutzer*innen zur Kompilierzeit nur ihre eigenen Assemblys (oder eine Teilmenge ihrer Assemblys) zur Verfügung stehen, müssen jedoch alle ihre Abhängigkeiten für die Laufzeit bereitstellen. In diesem Dokument werden Möglichkeiten zum Erreichen dieses Ergebnisses behandelt.

Es wird empfohlen, ein Paket pro Assembly und Paketabhängigkeiten für andere Assemblys zu verwenden. Wenn NuGet ein Projekt wiederherstellt, wird eine Ressourcenauswahl durchgeführt. Darüber hinaus können Sie verschiedene Ressourcenklassen einschließen, ausschließen oder als „privat“ festlegen. Um zu verhindern, dass die Abhängigkeiten Ihres Pakets zu Kompilierzeitressourcen für Personen werden, die Ihr Paket verwenden, können Sie die Einstellung für compile-Objekte in „privat“ ändern. Im generierten Paket führt dies dazu, dass compile aus der Abhängigkeit ausgeschlossen wird. Beachten Sie, dass die Standardeinstellung für private Ressourcen contentfiles;build;analyzers lautet, wenn kein anderer Wert angegeben ist. Aus diesem Grund sollten Sie PrivateAssets="compile;contentfiles;build;analyzers" in PackageReference oder ProjectReference verwenden.

<ItemGroup>
  <ProjectReference Include="..\OtherProject\OtherProject.csproj" PrivateAssets="compile;contentfiles;build;analyzers" />
  <PackageReference Include="SomePackage" Version="1.2.3" PrivateAssets="compile;contentfiles;build;analyzers" />
</ItemGroup>

Wenn Sie ein Paket aus einer benutzerdefinierten nuspec-Datei erstellen, anstatt NuGet automatisch ein Paket für Sie generieren zu lassen, sollte nuspec das XML-Attribut exclude verwenden.

<dependencies>
  <group targetFramework=".NETFramework4.8">
    <dependency id="OtherProject" version="3.2.1" exclude="Compile,Build,Analyzers" />
    <dependency id="SomePackage" version="1.2.3" exclude="Compile,Build,Analyzers" />
  </group>
</dependencies>

Es gibt drei Gründe, warum dies die empfohlene Lösung ist.

Erstens werden nützliche Assemblys häufig von neuen Assemblys bzw. Paketen referenziert. Obwohl eine Hilfsprogrammassembly aktuell möglicherweise nur von einem einzigen Paket verwendet werden soll, ist es verlockend, beide Assemblys in einem einzelnen Paket zu platzieren. Wenn ein zweites Paket die „private“ Hilfsprogrammassembly in Zukunft verwenden möchte, muss entweder die Hilfsprogrammassembly in ein neues Paket verschoben werden, und das alte Paket muss aktualisiert werden, um es als Abhängigkeit zu deklarieren, oder das Hilfsprogrammpaket muss sowohl im vorhandenen als auch im neuen Paket enthalten sein. Wenn die Assembly in zwei verschiedenen Paketen enthalten ist und ein Projekt auf beide Pakete verweist, kann NuGet die Versionsverwaltung nicht unterstützen, wenn es sich in den beiden Paketen um unterschiedliche Versionen der Hilfsprogrammassembly handelt.

Zweitens kann es vorkommen, dass Entwickler*innen, die Ihr Paket verwenden, auch APIs aus Ihren Abhängigkeiten verwenden möchten. Sehen Sie sich beispielsweise das Paket Microsoft.ServiceHub.Client, Version 3.0.3078 an. Wenn Sie das Paket herunterladen und die nuspec-Datei überprüfen, erkennen Sie, dass zwei Pakete aufgelistet sind, die mit Microsoft.VisualStudio. als Abhängigkeiten beginnen. Dies bedeutet, dass sie zur Laufzeit benötigt werden, aber deren Kompilierungsressourcen ausgeschlossen werden. Aus diesem Grund verfügen Projekte, die „Microsoft.ServiceHub.Client“ verwenden, nicht über die in IntelliSense verfügbaren Visual Studio-APIs. Dies gilt auch bei Erstellung des Projekts, es sei denn, das Projekt installiert diese Pakete explizit. Dies ist der Vorteil, den eine Paketabhängigkeit mit einem Ausschlussobjekt hat. Wenn bei Projekten, bei denen Ihr Paket verwendet wird, auch Ihre Abhängigkeiten genutzt werden sollen, können Sie einen Verweis auf das Paket hinzufügen, um die APIs für diese selbst verfügbar zu machen.

Einige Paketautor*innen waren in der Vergangenheit hinsichtlich der Assemblyauswahl von NuGet für Pakete verwirrt, die mehr als ein Zielframework unterstützen, wenn ihr Paket auch mehrere Assemblys enthält. Wenn die Hauptassembly verschiedene Zielframeworks für die Hilfsprogrammassembly unterstützt, ist es möglicherweise nicht offensichtlich, in welchen lib/-Verzeichnissen alle Assemblys platziert werden sollen. Wenn jedes Paket nach Assemblyname getrennt wird, ist es intuitiver, in welchen lib/-Ordnern die einzelnen Assemblys platziert werden sollen. Beachten Sie, dass dies nicht bedeutet, dass die Pakete Package1.net48 und Package1.net6.0 genutzt werden. Vielmehr sind lib/net48/Package1.dll und lib/net6.0/Package6.0 in Package1 bzw. lib/netstandard2.0/Package2.dll und lib/net5.0/Package2.dll in Package2 enthalten. Wenn NuGet ein Projekt wiederherstellt, führt NuGet die Ressourcenauswahl für die beiden Pakete unabhängig durch.

Beachten Sie auch, dass Ressourcen zum Einschließen bzw. Ausschließen von Abhängigkeiten nur von Projekten verwendet werden, die „PackageReference“ nutzen. Jedes Projekt, das Ihr Paket mithilfe von packages.config installiert, installiert Ihre Abhängigkeiten und stellt auch die zugehörigen APIs zur Verfügung. packages.config wird nur von älteren .NET Framework-Projektvorlagen von Visual Studio unterstützt. Projekte im SDK-Stil (und somit auch auf das .NET Framework ausgerichtete Projekte) bieten keine Unterstützung für packages.config und unterstützen daher Ressourcen zum Einschließen bzw. Ausschließen von Abhängigkeiten.

PackageReference und packages.config umfassen unterschiedliche verfügbare Features. In Abhängigkeit davon, ob Sie Paketbenutzer*innen unterstützen möchten, die PackageReference, packages.config oder beides verwenden, ändert sich die Art, wie Sie Ihr Paket erstellen müssen.

Das MSBuild-Paketziel von NuGet unterstützt das Einschließen von Projektverweisen in das Paket nicht automatisch. Diese Projekte, auf die verwiesen wird, werden nur als Paketabhängigkeiten aufgeführt. Es gibt ein Issue auf GitHub, über das Communitymitglieder Möglichkeiten geteilt haben, wie sie dieses Ergebnis erreicht haben. Dazu zählt in der Regel die Verwendung der PackagePath-MSBuild-Elementmetadaten zum Platzieren von Dateien an einer beliebigen Stelle im Paket (beschrieben in der Dokumentation zum Einschließen von Inhalten in ein Paket) sowie die Verwendung von SuppressDependenciesWhenPacking, um zu vermeiden, dass die Projektverweise zu Paketabhängigkeiten werden. Es gibt auch von der Community entwickelte Tools, die als Alternative zum offiziellen Paket von NuGet verwendet werden können und dieses Feature unterstützen.

PackageReference-Unterstützung

Wenn Paketbenutzer*innen PackageReference verwenden, wählt NuGet wie zuvor beschrieben die Kompilierungs- und Laufzeitressourcen unabhängig voneinander aus.

Kompilierungsressourcen bevorzugen ref/<tfm>/*.dll (z. B. ref/net6.0/*.dll), aber wenn dieses Element nicht vorhanden ist, wird stattdessen lib/<tfm>/*.dll (z. B. lib/net6.0/*.dll) verwendet.

Laufzeitressourcen bevorzugen runtimes/<rid>/lib/<tfm>/*.dll (z. B. runtimes/win11-x64/lib/net6.0/*.dll), aber wenn dieses Element nicht vorhanden ist, wird stattdessen lib/<tfm>/*.dll verwendet.

Da Assemblys in ref\<tfm>\ nicht zur Laufzeit verwendet werden, kann es sich um reine Metadatenassemblys zum Verringern der Paketgröße handeln.

packages.config-Unterstützung

Projekte mit Verwendung von packages.config zum Verwalten von NuGet-Paketen fügen normalerweise Verweise auf alle Assemblys im lib\<tfm>\-Verzeichnis hinzu. Das ref\-Verzeichnis wurde hinzugefügt, um PackageReference zu unterstützen und wird deshalb bei Verwendung von packages.config nicht berücksichtigt. Um explizit über packages.config festzulegen, welche Assemblys für Projekte referenziert werden, muss das Paket das <references>-Element in der nuspec-Datei verwenden. Zum Beispiel:

<references>
    <group targetFramework="net45">
        <reference file="MyLibrary.dll" />
    </group>
</references>

Die MSBuild-Paketziele unterstützen das <references>-Element nicht. Weitere Informationen zur Verwendung des MSBuild-Pakets finden Sie unter Packen mithilfe einer NUSPEC-Datei.

Hinweis

Das Projekt packages.config verwendet einen Prozess namens ResolveAssemblyReference, um Assemblys in das Ausgabeverzeichnis bin\<configuration>\ zu kopieren. Ihre Projektassembly wird kopiert, anschließend sucht das Buildsystem im Assemblymanifest nach referenzierten Assemblys und kopiert diese Assemblys. Dieser Vorgang wird rekursiv für alle Assemblys wiederholt. Dies bedeutet Folgendes: Wenn eine der Assemblys nur durch Reflektion geladen wird (Assembly.Load, MEF oder ein anderes Framework für die Abhängigkeitsinjektion), wird sie möglicherweise nicht in das bin\<configuration>\-Ausgabeverzeichnis Ihres Projekts kopiert, obwohl sie in bin\<tfm>\ vorhanden ist. Dies bedeutet auch, dass dies nur für .NET-Assemblys funktioniert, nicht für nativen Code, der mit P/Invoke aufgerufen wird.

Unterstützen von PackageReference und packages.config

Wichtig

Wenn ein Paket das NUSPEC-Element <references> umfasst und in ref\<tfm>\ keine Assemblys enthält, kündigt NuGet die im NUSPEC-Element <references> aufgeführten Assemblys sowohl als Kompilierzeit- als auch als Laufzeitressourcen an. Dies bedeutet, dass es zu Laufzeitausnahmen kommt, wenn die referenzierten Assemblys eine andere Assembly im lib\<tfm>\-Verzeichnis laden müssen. Daher ist es wichtig, sowohl das NUSPEC-Element <references> für die Unterstützung von packages.config zu verwenden als auch Assemblys im Ordner ref/ für die Unterstützung von PackageReference zu duplizieren. Der Paketordner runtimes/ muss nicht verwendet werden. Er wurde aus Gründen der Vollständigkeit dem obigen Abschnitt hinzugefügt.

Beispiel

Das Beispielpaket enthält drei Assemblys – MyLib.dll, MyHelpers.dll und MyUtilities.dll –, die auf .NET Framework 4.7.2 ausgerichtet sind. MyUtilities.dll enthält Klassen, die nur von den anderen zwei Assemblys verwendet werden sollen, deshalb sollen diese Klassen nicht in IntelliSense oder zur Kompilierzeit für Projekte zur Verfügung stehen, die das Beispielpaket verwenden. Die Datei nuspec muss folgende XML-Elemente enthalten:

<references>
    <group targetFramework="net472">
        <reference file="MyLib.dll" />
        <reference file="MyHelpers.dll" />
    </group>
</references>

Ich muss sicherstellen, dass mein Paket Folgendes umfasst:

lib\net472\MyLib.dll
lib\net472\MyHelpers.dll
lib\net472\MyUtilities.dll
ref\net472\MyLib.dll
ref\net472\MyHelpers.dll