Condividi tramite


Come NuGet risolve le dipendenze dei pacchetti

Ogni volta che un pacchetto viene installato o reinstallato, incluso l'installazione come parte di un processo di ripristino , NuGet installa anche eventuali pacchetti aggiuntivi da cui dipende il primo pacchetto.

Tali dipendenze immediate potrebbero quindi avere a loro volta altre dipendenze, che possono estendersi fino a una profondità arbitraria. Questo produce ciò che viene definito grafico delle dipendenze che descrive le relazioni tra i pacchetti a tutti i livelli.

Quando più pacchetti hanno la stessa dipendenza, lo stesso ID pacchetto può essere visualizzato più volte nel grafico, potenzialmente con vincoli di versione diversi. Tuttavia, in un progetto può essere usata una sola versione di un determinato pacchetto, quindi NuGet deve scegliere quale versione viene usata. Il processo esatto dipende dal formato di gestione dei pacchetti in uso.

Risoluzione delle dipendenze con PackageReference

Quando si installano pacchetti in progetti usando il formato PackageReference, NuGet aggiunge riferimenti a un grafico di pacchetto flat nel file appropriato e risolve i conflitti in anticipo. Questo processo viene definito ripristino transitivo. La reinstallazione o il ripristino dei pacchetti è quindi un processo di download dei pacchetti elencati nel grafico, con conseguente compilazione più veloce e prevedibile.

È anche possibile sfruttare le versioni mobili, ad esempio 2.8.*, per evitare di modificare il progetto in modo da usare la versione più recente di un pacchetto. Quando si usano versioni mobili, è consigliabile abilitare la funzionalità dei file di blocco per garantire la ripetibilità.

Quando il processo di ripristino NuGet viene eseguito prima di una compilazione, risolve prima le dipendenze in memoria, quindi scrive il grafico risultante in un file denominato project.assets.json.

Il file assets si trova in MSBuildProjectExtensionsPath, che per impostazione predefinita corrisponde alla cartella 'obj' del progetto. MSBuild legge quindi questo file e lo converte in un set di cartelle in cui è possibile trovare i potenziali riferimenti e quindi li aggiunge all'albero del progetto in memoria.

Il file project.assets.json è temporaneo e non deve essere aggiunto al controllo del codice sorgente. Viene elencato per impostazione predefinita sia in .gitignore che in .tfignore. Consulta i pacchetti e il controllo del codice sorgente.

Regole di risoluzione delle dipendenze

Il ripristino transitivo applica quattro regole principali per risolvere le dipendenze: versione minima applicabile, versioni fluttuanti, prevalenza della dipendenza direttae dipendenze correlate .

Versione più bassa applicabile

La regola di versione più bassa applicabile ripristina la versione più bassa possibile di un pacchetto come definito dalle relative dipendenze. Si applica anche alle dipendenze dall'applicazione o dalla libreria di classi, a meno che non venga dichiarata come fluttuante.

Nella figura seguente, ad esempio, 1.0-beta è considerato inferiore a 1.0, quindi NuGet sceglie la versione 1.0:

Scelta della versione più bassa applicabile

Nella figura seguente la versione 2.1 non è disponibile nel feed, ma poiché il vincolo di versione è >= 2.1 NuGet seleziona la versione più bassa successiva che può trovare, in questo caso 2.2:

Scelta della versione più bassa successiva disponibile nel feed

Quando un'applicazione specifica un numero di versione esatto, ad esempio 1.2, che non è disponibile nel feed, NuGet ha esito negativo e viene visualizzato un errore quando si tenta di installare o ripristinare il pacchetto:

NuGet genera un errore quando non è disponibile una versione esatta del pacchetto

Versioni fluttuanti

Una versione di dipendenza fluttuante viene specificata con il carattere * . Ad esempio, 6.0.*. Questa specifica di versione indica "usare la versione 6.0.x più recente"; 4.* significa "usare la versione 4.x più recente." L'uso di una versione fluttuante riduce le modifiche apportate al file di progetto, mantenendo al tempo stesso la dipendenza aggiornata all'ultima versione. Le versioni mobili possono essere specificate solo a livello di progetto.

Quando si usa una versione fluttuante, NuGet risolve la versione più alta di un pacchetto che corrisponde al modello di versione, ad esempio 6.0.* ottiene la versione più alta di un pacchetto che inizia con 6.0:

Scelta di 6.0.1 quando viene richiesta una versione fluttuante 6.0.*

Versione Versioni presenti nel server Risoluzione Ragione Note
* 1.1.0
1.1.1
1.2.0
1.3.0-alpha
1.2.0 Versione stabile più recente.
1.1.* 1.1.0
1.1.1
1.1.2-alpha
1.2.0-alpha
1.1.1 Versione stabile più alta che rispetta il modello specificato.
*-* 1.1.0
1.1.1
1.1.2-alpha
1.3.0-beta
1.3.0-beta Versione più recente, incluse le versioni non stabili. Disponibile in Visual Studio versione 16.6, NuGet versione 5.6, .NET Core SDK versione 3.1.300
1.1.*-* 1.1.0
1.1.1
1.1.2-alpha
1.1.2-beta
1.3.0-beta
1.1.2-beta La versione più alta rispetta il modello e include le versioni non stabili. Disponibile in Visual Studio versione 16.6, NuGet versione 5.6, .NET Core SDK versione 3.1.300
1.2.0-rc.* 1.1.0
1.2.0-rc.1
1.2.0-rc.2
1.2.0
1.2.0 Nonostante questo sia un intervallo di versioni con una parte non definitiva, le versioni stabili sono consentite se corrispondono alla parte stabile. Dato che 1.2.0 > 1.2.0-rc.2, è stato scelto.

Nota

La risoluzione della versione fluttuante non tiene conto se un pacchetto è elencato oppure no. La versione fluttuante sarà risolta localmente se le condizioni possono essere soddisfatte con i pacchetti nella Cartella Globale dei Pacchetti.

Prevale la dipendenza diretta

Quando il grafico del pacchetto per un'applicazione contiene versioni diverse di un pacchetto nello stesso sottografo e una di queste versioni è una dipendenza diretta in tale sottografo, tale versione verrà scelta per tale sottografo e il resto verrà ignorato. Questo comportamento consente a un'applicazione di eseguire l'override di una determinata versione del pacchetto nel grafico delle dipendenze.

Nell'esempio seguente l'applicazione dipende direttamente dal pacchetto B con un vincolo di versione di >=2.0.0. L'applicazione dipende anche dal pacchetto A, che a sua volta dipende anche dal pacchetto B, ma con un vincolo >=1.0.0. Poiché la dipendenza dal pacchetto B 2.0.0 è dipendenza diretta dall'applicazione nel grafico, tale versione viene usata:

'applicazione che usa la regola direct dependency wins

Avvertimento

La regola di vittoria delle dipendenze dirette può comportare un downgrade della versione del pacchetto, con conseguente potenziale interruzione di altre dipendenze nel grafico. Quando viene effettuato il downgrade di un pacchetto, NuGet aggiunge un avviso di per avvisare l'utente.

Questa regola comporta anche una maggiore efficienza con un grafico delle dipendenze di grandi dimensioni. Quando una dipendenza più vicina nello stesso sottografo ha una versione successiva rispetto a un'altra, NuGet ignora tale dipendenza e NuGet ignora anche tutte le dipendenze rimanenti in tale ramo del grafo.

Nel diagramma seguente, ad esempio, poiché viene usato Package C 2.0.0, NuGet ignora tutti i rami in tale sottografo che fanno riferimento a una versione precedente del pacchetto C:

Quando NuGet ignora un pacchetto nel grafico, ignora l'intero ramo

Tramite questa regola, NuGet tenta di rispettare la finalità dell'autore del pacchetto. Nel diagramma seguente, l'autore del pacchetto A ha effettuato il downgrade esplicito al pacchetto C 1.0.0 dal pacchetto C 2.0.0.

Quando un autore di pacchetti effettua il downgrade esplicito, NuGet lo rispetta.

Il proprietario dell'applicazione può scegliere di aggiornare il pacchetto C a una versione successiva alla versione 2.0.0, senza quindi eseguire il downgrade della versione per Package C. In questo caso, non viene generato alcun avviso.

Quando un'applicazione aggiunge una dipendenza diretta per un pacchetto declassato, NuGet lo rispetta.

Dipendenze cugine

Quando si fa riferimento a versioni di pacchetti diverse in sottogrammi diversi nel grafico dell'applicazione, NuGet usa la versione più bassa che soddisfa tutti i requisiti di versione (come con la regola della versione più bassa applicabile e le regole delle versioni fluttuanti ). Nell'immagine seguente, ad esempio, la versione 2.0.0 del pacchetto B soddisfa l'altro vincolo >=1.0.0 e viene quindi usato:

Risoluzione delle dipendenze cugine usando la versione precedente che soddisfa tutti i vincoli

Si noti che i pacchetti non devono essere sulla stessa distanza per applicare la regola delle dipendenze cugine. Nel diagramma seguente, il pacchetto D 2.0.0 viene scelto nel sottografo Package C e package D 3.0.0 viene scelto nel sottografo di Package A. Nel sottografo Dell'applicazione non esiste alcuna dipendenza diretta dal pacchetto D, quindi viene scelta la versione più bassa applicabile regola e viene scelta la versione 3.0.0.

Risoluzione delle dipendenze cugine usando la versione inferiore che soddisfa tutti i vincoli a distanze diverse

In alcuni casi, non è possibile soddisfare tutti i requisiti di versione. Come illustrato di seguito, se il pacchetto A richiede esattamente il pacchetto B 1.0.0 e il pacchetto C richiede il pacchetto B >=2.0.0, NuGet non può risolvere le dipendenze e restituisce un errore.

dipendenze non risolvibili a causa di un requisito di versione esatto

In queste situazioni, il consumer di primo livello (l'applicazione o il pacchetto) deve aggiungere la propria dipendenza diretta dal pacchetto B affinché si applichi la regola della vittoria della dipendenza diretta di .

Intervalli di versioni e versioni non definitive con PackageReference

Non è insolito che un pacchetto disponga di versioni stabili e prerelease disponibili. Quando si risolve un grafo delle dipendenze, NuGet decide se prendere in considerazione le versioni non definitive per un pacchetto in base a una singola regola: If the project or any packages within the graph request a prerelease version of a package, then include both prerelease or stable versions, otherwise consider stable versions only.

In pratica, in base alla regola più bassa applicabile, ciò significa:

Intervallo di versioni Versioni disponibili Versione selezionata
[1.0.0, 2.0.0) 1.2.0-beta.1, 1.2.0, 1.2.0
[1.0.0, 2.0.0-0) 1.2.0-beta.1, 1.2.0, 1.2.0-beta.1
[1.0.0, 2.0.0) 1.2.0-beta.1, 2.0.0-beta.3 Nessuno, viene generato NU1103.
[1.0.0, 2.0.0-rc) 1.2.0-beta.1, 2.0.0-beta.3 1.2.0-beta.1

Risoluzione delle dipendenze con packages.config

Con packages.config, le dipendenze di un progetto vengono scritte in packages.config come elenco flat. Tutte le dipendenze di tali pacchetti vengono scritte anche nello stesso elenco. Quando vengono installati i pacchetti, NuGet potrebbe anche modificare il file .csproj, app.config, web.confige altri singoli file.

Con packages.config, NuGet tenta di risolvere i conflitti di dipendenza durante l'installazione di ogni singolo pacchetto. Ovvero, se il pacchetto A viene installato e dipende dal pacchetto B e il pacchetto B è già elencato in packages.config come dipendenza da qualcos'altro, NuGet confronta le versioni del pacchetto B richieste e tenta di trovare una versione che soddisfi tutti i vincoli di versione. In particolare, NuGet seleziona la versione inferiore major.minor che soddisfa le dipendenze.

Per impostazione predefinita, NuGet 2.8 cerca la versione più bassa della patch (vedere note di rilascio di NuGet 2.8). È possibile controllare questa impostazione tramite l'attributo DependencyVersion in NuGet.Config e l'opzione -DependencyVersion sulla riga di comando.

Il processo di packages.config per la risoluzione delle dipendenze diventa complicato per i grafici delle dipendenze più grandi. Ogni nuova installazione del pacchetto richiede un attraversamento dell'intero grafico e genera la possibilità di conflitti di versione. Quando si verifica un conflitto, l'installazione viene arrestata, lasciando il progetto in uno stato indeterminato, soprattutto con potenziali modifiche al file di progetto stesso. Questo non è un problema quando si usano altri formati di gestione dei pacchetti.

Intervalli di versioni e versioni non definitive con packages.config

packages.config risoluzione non consente la combinazione di dipendenze stabili e non definitive in un grafico. Se una dipendenza viene definita nell'intervallo come [1.0.0, 2.0.0), i pacchetti pre-release non sono consentiti nel grafo.

Gestione degli asset di dipendenza

Quando si usa il formato PackageReference, è possibile controllare quali asset dalle dipendenze passano al progetto di primo livello. Per informazioni dettagliate, vedere PackageReference.

Quando il progetto di primo livello è un pacchetto, è anche possibile controllare questo flusso usando gli attributi include e exclude con dipendenze elencate nel file .nuspec. Consulta il riferimento .nuspec - Dipendenze.

Esclusione di riferimenti

Esistono scenari in cui è possibile fare riferimento agli assembly con lo stesso nome più di una volta in un progetto, generando errori in fase di progettazione e in fase di compilazione. Si consideri un progetto che contiene una versione personalizzata di C.dlle fa riferimento al pacchetto C che contiene anche C.dll. Allo stesso tempo, il progetto dipende anche dal pacchetto B, che dipende anche dal pacchetto C e C.dll. Di conseguenza, NuGet non è in grado di determinare quale C.dll usare, ma non è possibile rimuovere solo la dipendenza del progetto dal pacchetto C perché il pacchetto B dipende anche da esso.

Per risolvere questo problema, è necessario fare riferimento direttamente alla C.dll desiderata (o usare un altro pacchetto che fa riferimento a quello corretto) e quindi aggiungere una dipendenza da Package C che esclude tutti gli asset. Questa operazione viene eseguita come segue a seconda del formato di gestione dei pacchetti in uso:

  • PackageReference: aggiungi ExcludeAssets="All" alla dipendenza:

    <PackageReference Include="PackageC" Version="1.0.0" ExcludeAssets="All" />
    
  • packages.config: rimuovere il riferimento a PackageC dal file di .csproj in modo che faccia riferimento solo alla versione di C.dll desiderata.

Aggiornamenti delle dipendenze durante l'installazione dei pacchetti

Se una versione di dipendenza è già soddisfatta, la dipendenza non viene aggiornata durante altre installazioni di pacchetti. Si consideri ad esempio il pacchetto A che dipende dal pacchetto B e specifica 1.0 per il numero di versione. Il repository di origine contiene le versioni 1.0, 1.1 e 1.2 del pacchetto B. Se A è installato in un progetto che contiene già B versione 1.0, B 1.0 rimane in uso perché soddisfa il vincolo di versione. Tuttavia, se il pacchetto A richiede la versione 1.1 o successiva di B, verrà installato B 1.2.

Risoluzione degli errori di pacchetto incompatibili

Durante un'operazione di ripristino dei pacchetti, è possibile che venga visualizzato l'errore "Uno o più pacchetti non sono compatibili..." o che un pacchetto "non è compatibile" con il framework di destinazione del progetto.

Questo errore si verifica quando uno o più pacchetti a cui si fa riferimento nel progetto non indicano che supportano il framework di destinazione del progetto; ovvero, il pacchetto non contiene una DLL appropriata nella relativa cartella lib per un framework di destinazione compatibile con il progetto. Vedere Framework di Destinazione per un elenco.

Ad esempio, se un progetto è destinato a netstandard1.6 e si tenta di installare un pacchetto contenente DLL solo nelle cartelle lib\net20 e \lib\net45, vengono visualizzati messaggi simili al seguente per il pacchetto ed eventualmente i relativi dipendenti:

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

Per risolvere le incompatibilità, eseguire una delle operazioni seguenti:

  • Riadattare il progetto a un framework supportato dai pacchetti che si desidera utilizzare.
  • Contattare l'autore dei pacchetti e collaborare con loro per aggiungere il supporto per il framework scelto. A questo scopo, ogni pagina di presentazione di pacchetti in nuget.org include un collegamento Contact Owners.

Consiglio

soluzione alternativa: NuGetSolver è un'estensione di Visual Studio sviluppata da Microsoft DevLabs, progettata per facilitare la risoluzione dei conflitti di dipendenza. Automatizza il processo di identificazione e risoluzione di questi problemi. Per ulteriori dettagli, visitare la pagina NuGetSolver su Visual Studio Marketplace e ci piacerebbe ricevere i tuoi commenti e suggerimenti sulla tua esperienza.