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:
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:
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:
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:
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:
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:
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.
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.
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:
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.
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.
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:
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.config
e 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.dll
e 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 diC.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.