Risoluzione delle dipendenze dei pacchetti in NuGet

Ogni volta che un pacchetto viene installato o reinstallato, compresa l'installazione nell'ambito di un processo di ripristino, NuGet installa anche eventuali altri pacchetti da cui dipende questo primo pacchetto.

Tali dipendenze immediate potrebbero quindi avere anche dipendenze proprie, che possono continuare fino a una profondità arbitraria. Ciò produce un cosiddetto grafico dipendenze, che descrive le relazioni tra i pacchetti a tutti i livelli.

Quando più pacchetti hanno la stessa dipendenza, lo stesso ID di pacchetto può essere visualizzato nel grafico più volte, potenzialmente con limitazioni delle versioni diverse. Tuttavia, solo una versione di un determinato pacchetto può essere usata in un progetto, pertanto NuGet deve scegliere quale versione usare. 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 semplice nel file appropriato e risolve i conflitti in anticipo. Questo processo è noto come ripristino transitivo. La reinstallazione o il ripristino dei pacchetti è quindi un processo di download dei pacchetti elencati nel grafico, che genera compilazioni più veloci e più prevedibili.

È 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à del file di blocco per garantire la ripetibilità.

Quando il processo di ripristino di 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 di asset 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 sono disponibili riferimenti potenziali, aggiungendoli quindi 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 in .gitignore e .tfignore. Vedere Pacchetti e controllo del codice sorgente.

Regole per la risoluzione delle dipendenze

Il ripristino transitivo applica quattro regole principali per risolvere le dipendenze: versione più bassa applicabile, versioni mobili, direct-dependency-wins e dipendenze cugine.

Versione più bassa applicabile

La regola della versione più bassa applicabile ripristina la versione più bassa possibile di un pacchetto come definito dalle relative dipendenze. Si applica anche alle dipendenze per l'applicazione o la libreria di classi a meno che non siano dichiarate mobili.

Nella figura seguente, ad esempio, la versione beta 1.0 viene considerata inferiore alla versione 1.0, pertanto NuGet sceglie la versione 1.0:

Choosing the lowest applicable version

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:

Choosing the next lowest version available on the feed

Quando un'applicazione specifica un numero di versione esatto, ad esempio 1.2, che non è disponibile nel feed, NuGet genera un errore durante il tentativo di installare o ripristinare il pacchetto:

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

Versioni mobili

Una versione di dipendenza mobile 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 mobile riduce le modifiche apportate al file di progetto, mantenendo al tempo stesso aggiornato la versione più recente di una dipendenza. Le versioni mobili possono essere specificate solo a livello di progetto.

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

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

Versione Versioni presenti nel server Risoluzione Motivo 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

Nota

La risoluzione della versione mobile non tiene conto se un pacchetto è elencato o meno. La risoluzione della versione mobile verrà risolta in locale se le condizioni possono essere soddisfatte con i pacchetti nella cartella del pacchetto globale.

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 qualsiasi versione del pacchetto specifica nel grafico dipendenze.

Nell'esempio seguente l'applicazione dipende direttamente dal pacchetto B con un vincolo di >versione =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:

Application using the Direct dependency wins rule

Avviso

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

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

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.

When a package author explicitly downgrades, NuGet honors that.

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.

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

Dipendenze prossime

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

Resolving cousin dependencies using the lower version that satisfies all constraints

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 applicata la regola della versione più bassa applicabile e viene scelta la versione 3.0.0.

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

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 è in grado di risolvere le dipendenze e restituisce un errore.

Unresolvable dependencies due to an exact version requirement

In queste situazioni, il consumer di primo livello (l'applicazione o il pacchetto) deve aggiungere la propria dipendenza diretta dal pacchetto B in modo che venga applicata la regola Di dipendenza diretta.

Risoluzione delle dipendenze con packages.config

Con packages.config, le dipendenze di un progetto vengono scritte in packages.config sotto forma di elenco semplice. Anche le eventuali dipendenze di questi pacchetti vengono scritte nello stesso elenco. Quando i pacchetti vengono installati, NuGet può anche modificare il file .csproj, app.config, web.config e altri file singoli.

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 di un altro elemento, NuGet confronta le versioni del pacchetto B richieste e tenta di trovare una versione che soddisfi tutte le limitazioni delle versioni. In particolare, NuGet seleziona la versione più bassa di major.minor che soddisfa le dipendenze.

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

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

Gestione degli asset delle dipendenze

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

Quando il progetto di primo livello è un pacchetto, è anche possibile avere il controllo di questo flusso usando gli attributi include e exclude con le dipendenze elencate nel file .nuspec. Vedere la sezione Dipendenze delle informazioni di riferimento sul file .nuspec.

Esclusione dei riferimenti

Esistono scenari in cui agli assembly con lo stesso nome potrebbe essere fatto riferimento più volte 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 che fa riferimento al pacchetto C contenente anche C.dll. Al tempo stesso, il progetto dipende anche dal pacchetto B, che dipende anche dal pacchetto C e da C.dll. Di conseguenza, NuGet non riesce a determinare quale C.dll usare, ma è possibile rimuovere semplicemente la dipendenza del progetto dal pacchetto C perché anche il pacchetto B dipende da esso.

Per risolvere questa situazione, è necessario fare riferimento direttamente al C.dll desiderato (o usare un altro pacchetto che faccia riferimento a quello giusto) e quindi aggiungere una dipendenza dal pacchetto C che escluda tutti i relativi asset. Questa operazione viene eseguita come indicato di seguito a seconda del formato di gestione dei pacchetti in uso:

  • PackageReference: aggiungere ExcludeAssets="All" nella dipendenza:

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

Aggiornamenti delle dipendenze durante l'installazione dei pacchetti

Se una versione della dipendenza è già soddisfatta, la dipendenza non viene aggiornata durante le altre installazioni del pacchetto. 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 il pacchetto A è installato in un progetto che contiene già la versione 1.0 del pacchetto B, il pacchetto B 1.0 rimane in uso perché soddisfa il vincolo di versione. Tuttavia, se un pacchetto A richiede la versione 1.1 o versioni successive del pacchetto B, verrà installata la versione 1.2 del pacchetto B.

Risoluzione degli errori dei pacchetti incompatibili

Durante un'operazione di ripristino di un pacchetto, potrebbe essere visualizzato un errore analogo a "Uno o più pacchetti non sono compatibili..." o un avviso che un pacchetto "non è compatibile" con il framework di destinazione del progetto.

Questo errore si verifica quando uno o più dei pacchetti a cui si fa riferimento nel progetto non indicano che supportano il framework di destinazione del progetto, vale a dire 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 ha come destinazione netstandard1.6 e si tenta di installare un pacchetto che contiene 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:

  • Ridestinare il progetto per un framework supportato dai pacchetti da usare.
  • Contattare l'autore dei pacchetti e collaborare per aggiungere il supporto per il framework scelto. Nella pagina di presentazione di tutti i pacchetti su nuget.org è incluso un collegamento Contact Owners (Contatta proprietari) a questo scopo.

Suggerimento

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 altri dettagli, visitare la pagina NuGetSolver in Visual Studio Marketplace e si vuole ricevere commenti e suggerimenti sull'esperienza.