Selezionare gli assembly cui i progetti fanno riferimento

Gli assembly vengono usati in due modi diversi durante una compilazione. Il primo è per la compilazione, che consente al codice del consumer del pacchetto di compilare in base alle API nell'assembly e per IntelliSense per fornire suggerimenti. Il secondo è il runtime, in cui l'assembly viene copiato nella directory e viene usato durante l'esecuzione bin del programma. Alcuni autori di pacchetti desiderano solo i propri assembly (o un subset degli assembly) disponibili per i consumer di pacchetti in fase di compilazione, ma devono fornire tutte le relative dipendenze per il runtime. Questo documento esamina i modi per ottenere questo risultato.

È consigliabile avere un pacchetto per assembly e le dipendenze dei pacchetti ad altri assembly. Quando NuGet ripristina un progetto, esegue la selezione degli asset e supporta l'inclusione, l'esclusione e la creazione di classi di asset diverse private. Per evitare che le dipendenze del pacchetto diventino asset in fase di compilazione per chiunque usi il pacchetto, è possibile rendere compile privati gli asset. Nel pacchetto generato, che causerà compile l'esclusione dalla dipendenza. Si noti che gli asset privati predefiniti quando non viene fornito nessuno è contentfiles;build;analyzers. Pertanto, è consigliabile usare PrivateAssets="compile;contentfiles;build;analyzers" in PackageReference o ProjectReference.

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

Se si crea un pacchetto da un file personalizzato nuspec , anziché lasciare che NuGet generi automaticamente un pacchetto, è consigliabile nuspec usare l'attributo exclude XML .

<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>

Esistono tre motivi per cui questa è la soluzione consigliata.

In primo luogo, gli assembly utili vengono spesso a cui viene fatto riferimento da nuovi assembly/pacchetti. Anche se un assembly di utilità potrebbe essere usato solo da un singolo pacchetto, rendendo la tentazione di spedire entrambi gli assembly in un singolo pacchetto, se un secondo pacchetto vuole usare l'assembly dell'utilità "privato" in futuro, l'assembly dell'utilità deve essere spostato in un nuovo pacchetto e il pacchetto precedente deve essere aggiornato per dichiararlo come dipendenza, o il pacchetto dell'utilità deve essere fornito sia nel pacchetto esistente che nel nuovo pacchetto. Se l'assembly viene fornito in due pacchetti diversi e un progetto fa riferimento a entrambi i pacchetti, se sono presenti versioni diverse dell'assembly di utilità nei due pacchetti, NuGet non sarà in grado di facilitare la gestione delle versioni.

In secondo luogo, possono verificarsi momenti in cui gli sviluppatori che usano il pacchetto vogliono usare anche le API dalle dipendenze. Si consideri ad esempio il pacchetto Microsoft.ServiceHub.Client versione 3.0.3078. Se si scarica il pacchetto e si controlla il nuspec file, è possibile notare che elenca due pacchetti che iniziano con Microsoft.VisualStudio. come dipendenze, ovvero ne hanno bisogno in fase di esecuzione, ma esclude anche gli asset di compilazione. Ciò significa che i progetti che usano Microsoft.ServiceHub.Client non avranno le API di Visual Studio disponibili in IntelliSense o se compilano il progetto, a meno che il progetto non installi esplicitamente tali pacchetti. E questo è il vantaggio che una dipendenza del pacchetto con un asset di esclusione ha. I progetti che usano il pacchetto, se vogliono usare anche le dipendenze, possono aggiungere un riferimento al pacchetto per rendere le API disponibili per se stessi.

Infine, alcuni autori di pacchetti sono stati confusi in passato sulla selezione dell'assembly di NuGet per i pacchetti che supportano più framework di destinazione quando il pacchetto contiene anche più assembly. Se l'assembly principale supporta framework di destinazione diversi per l'assembly di utilità, potrebbe non essere ovvio in quali lib/ directory inserire tutti gli assembly. Separando ogni pacchetto in base al nome dell'assembly, è più intuitivo in quali lib/ cartelle ogni assembly deve essere inserito. Si noti che questo non significa avere Package1.net48 pacchetti e Package1.net6.0 . Significa avere lib/net48/Package1.dll e lib/net6.0/Package6.0 in Package1e lib/netstandard2.0/Package2.dll in lib/net5.0/Package2.dllPackage2. Quando NuGet ripristina un progetto, Nuget eseguirà in modo indipendente la selezione degli asset per i due pacchetti.

Si noti anche che le risorse di inclusione/esclusione delle dipendenze vengono usate solo dai progetti che usano PackageReference. Qualsiasi progetto che installa il pacchetto usando packages.config installerà le dipendenze e ne avrà anche le API disponibili. packages.config è supportato solo dai modelli di progetto .NET Framework precedenti di Visual Studio. I progetti in stile SDK, anche quelli destinati a .NET Framework, non supportano packages.confige quindi supportano le risorse di inclusione/esclusione delle dipendenze.

PackageReference e packages.config hanno diverse funzionalità disponibili. Se si vuole supportare i consumer di pacchetti che usano PackageReference, packages.configo entrambi, cambia il modo in cui è necessario creare il pacchetto.

La destinazione MSBuild Pack di NuGet non supporta automaticamente l'inclusione automatica dei riferimenti al progetto nel pacchetto. Verranno elencati solo i progetti a cui si fa riferimento come dipendenze del pacchetto. Si è verificato un problema in GitHub, in cui i membri della community hanno condiviso i modi in cui hanno ottenuto questo risultato, che in genere implica l'uso dei PackagePath metadati dell'elemento MSBuild per inserire i file in qualsiasi punto del pacchetto, come descritto nella documentazione sull'inclusione del contenuto in un pacchetto e l'uso SuppressDependenciesWhenPacking di per evitare che i riferimenti al progetto diventino dipendenze del pacchetto. Esistono anche strumenti sviluppati dalla community che possono essere usati come alternativa al pacchetto ufficiale di NuGet, che supporta questa funzionalità.

Supporto di PackageReference

Quando un consumer di pacchetti usa PackageReference, NuGet seleziona gli asset di compilazione e runtime in modo indipendente, come descritto in precedenza.

Gli asset di compilazione preferiscono ref/<tfm>/*.dll (ad esempio ), ma se non esiste, eseguirà il fallback a lib/<tfm>/*.dll (ad esempio lib/net6.0/*.dllref/net6.0/*.dll).

Gli asset di runtime preferiscono runtimes/<rid>/lib/<tfm>/*.dll ,ad esempio (runtimes/win11-x64/lib/net6.0/*.dll), ma se non esiste, eseguirà il fallback a lib/<tfm>/*.dll.

Poiché gli assembly in ref\<tfm>\ non vengono usati al runtime, possono essere assembly di soli metadati in modo da ridurre le dimensioni del pacchetto.

Supporto di packages.config

I progetti che usano packages.config per gestire i pacchetti NuGet in genere aggiungono riferimenti a tutti gli assembly nella directory lib\<tfm>\. La directory ref\ è stata aggiunta per supportare PackageReference e pertanto non viene considerata quando si usa packages.config. Per impostare in modo esplicito gli assembly cui viene fatto riferimento per i progetti che usano packages.config, il pacchetto deve usare l'elemento <references> nel file nuspec. Ad esempio:

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

Le destinazioni del pacchetto MSBuild non supportano l'elemento <references> . Vedere la documentazione sulla compressione con un file con estensione nuspec quando si usa MSBuild Pack.

Nota

Il progetto packages.config usa un processo denominato ResolveAssemblyReference per copiare gli assembly nella directory di output bin\<configuration>\. L'assembly del progetto viene copiato, il sistema di compilazione esamina il manifesto dell'assembly per individuare gli assembly di riferimento e quindi copia gli assembly e ripete l'operazione in modo ricorsivo per tutti gli assembly. Ciò significa che se uno degli assembly caricati solo dalla reflection (Assembly.LoadMEF o da un altro framework di inserimento delle dipendenze), potrebbe non essere copiato nella directory di output del bin\<configuration>\ progetto nonostante sia presente in bin\<tfm>\. Ciò significa anche che funziona solo per gli assembly .NET, non per il codice nativo chiamato con P/Invoke.

Supporto di entrambi PackageReference e packages.config

Importante

Se un pacchetto contiene l'elemento nuspec <references> e non contiene assembly in ref\<tfm>\, NuGet annuncia gli assembly elencati nell'elemento nuspec <references> come asset di compilazione e di runtime. Ciò significa che vi saranno eccezioni di runtime quando gli assembly cui si fa riferimento dovranno caricare qualsiasi altro assembly nella directory lib\<tfm>\. Pertanto, è importante usare sia nuspec <references> per packages.config il supporto, sia la duplicazione di assembly nella cartella per PackageReference il ref/ supporto. La runtimes/ cartella del pacchetto non deve essere usata, è stata aggiunta alla sezione precedente per completezza.

Esempio

Il pacchetto conterrà tre assembly, MyLib.dll, MyHelpers.dll e MyUtilities.dll, destinati a .NET Framework 4.7.2. MyUtilities.dll contiene le classi destinate a essere usate solo dagli altri due assembly, pertanto l'utente non vuole che le classi vengano rese disponibili in IntelliSense o in fase di compilazione per i progetti che usano il pacchetto. Il file nuspec deve contenere gli elementi XML seguenti:

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

È necessario assicurarsi che il contenuto del pacchetto sia:

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