Como o NuGet resolve as dependências do pacote
Sempre que um pacote é instalado ou reinstalado, o que inclui ser instalado como parte de um processo de restauração , o NuGet também instala todos os pacotes adicionais dos quais o pacote original depende.
Essas dependências imediatas também podem ter dependências próprias, o que pode continuar a uma profundidade arbitrária. Isso produz um chamado grafo de dependência que descreve as relações entre pacotes em todos os níveis.
Quando vários pacotes têm a mesma dependência, a mesma ID do pacote pode aparecer no grafo várias vezes, potencialmente com restrições de versão diferentes. No entanto, apenas uma versão de um determinado pacote pode ser usada em um projeto, portanto, o NuGet deve escolher qual versão é usada. O processo exato depende do formato de gerenciamento de pacote que está sendo usado.
Resolução de dependência com PackageReference
Ao instalar pacotes em projetos usando o formato PackageReference, o NuGet adiciona referências a um grafo de pacote simples no arquivo apropriado e resolve conflitos antecipadamente. Esse processo é conhecido como de restauração transitiva. Reinstalar ou restaurar pacotes é então um processo de download dos pacotes listados no grafo, resultando em builds mais rápidos e previsíveis.
Você também pode aproveitar as versões flutuantes, como 2.8.*, para evitar modificar o projeto para usar a versão mais recente de um pacote. Ao usar versões flutuantes, recomendamos habilitar a funcionalidade de arquivo de bloqueio para garantir a repetição.
Quando o processo de restauração do NuGet é executado antes de um build, ele resolve as dependências primeiro na memória e grava o grafo resultante em um arquivo chamado project.assets.json
.
O arquivo de ativos está localizado em MSBuildProjectExtensionsPath
, que usa como padrão a pasta 'obj' do projeto.
O MSBuild lê esse arquivo e o converte em um conjunto de pastas em que possíveis referências podem ser encontradas e, em seguida, adiciona-as à árvore de projeto na memória.
O arquivo project.assets.json
é temporário e não deve ser adicionado ao controle do código-fonte. Ele é listado por padrão em .gitignore
e .tfignore
. Consulte Pacotes e controle de código-fonte.
Regras de resolução de dependência
A restauração transitiva aplica quatro regras principais para resolver dependências: versão mais baixa aplicável, versões flutuantes, prioridade para dependências diretas, e dependências colaterais.
Versão mais baixa aplicável
A regra de versão mais baixa aplicável restaura a versão mais baixa possível de um pacote, conforme definido por suas dependências. Ele também se aplica a dependências no aplicativo ou na biblioteca de classes, a menos que seja declarado como flutuante .
Na figura a seguir, por exemplo, 1.0-beta é considerado inferior a 1.0, portanto, o NuGet escolhe a versão 1.0:
Na próxima figura, a versão 2.1 não está disponível no feed, mas como a restrição de versão é >= 2.1 o NuGet escolhe a próxima versão mais baixa que pode encontrar, nesse caso 2.2:
Quando um aplicativo especifica um número de versão exato, como 1.2, que não está disponível no feed, o NuGet falha com um erro ao tentar instalar ou restaurar o pacote:
Versões flutuantes
Uma versão de dependência flutuante é especificada com o caractere * . Por exemplo, 6.0.*
. Esta especificação de versão diz "usar a versão 6.0.x mais recente"; 4.*
significa "usar a versão 4.x mais recente". O uso de uma versão flutuante reduz as alterações no arquivo de projeto, mantendo-se atualizado com a versão mais recente de uma dependência.
As versões flutuantes só podem ser especificadas no nível do projeto.
Ao usar uma versão flutuante, o NuGet resolve a versão mais alta de um pacote que corresponde ao padrão de versão, por exemplo, 6.0.*
obtém a versão mais alta de um pacote que começa com 6.0:
Versão | Versões presentes no servidor | Resolução | Razão | Anotações |
---|---|---|---|---|
* | 1.1.0 1.1.1 1.2.0 1.3.0-alfa |
1.2.0 | A versão estável mais recente. | |
1.1.* | 1.1.0 1.1.1 1.1.2-alfa 1.2.0-alpha |
1.1.1 | A versão estável mais alta que respeita o padrão especificado. | |
*-* | 1.1.0 1.1.1 1.1.2-alfa 1.3.0-beta |
1.3.0-beta | A versão mais alta, incluindo as versões não estáveis. | Disponível no Visual Studio versão 16.6, NuGet versão 5.6, SDK do .NET Core versão 3.1.300 |
1.1.*-* | 1.1.0 1.1.1 1.1.2-alfa 1.1.2-beta 1.3.0-beta |
1.1.2-beta | A versão mais alta que respeita o padrão e inclui as versões não estáveis. | Disponível no Visual Studio versão 16.6, NuGet versão 5.6, SDK do .NET Core versão 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 | Apesar de este ser um intervalo de versão com uma parte de pré-lançamento, as versões estáveis serão permitidas se corresponderem à parte estável. Considerando que 1.2.0 > 1.2.0-rc.2, ela foi a escolhida. |
Nota
A resolução de versão flutuante não leva em conta se um pacote está listado ou não. A resolução de versão flutuante ocorrerá localmente se as condições puderem ser atendidas com pacotes na Pasta Global de Pacotes.
A dependência direta vence
Quando o grafo de pacote de um aplicativo contém versões diferentes de um pacote no mesmo subgrafo e uma dessas versões é uma dependência direta nesse subgrafo, essa versão seria escolhida para esse subgrafo e o restante será ignorado. Esse comportamento permite que um aplicativo substitua qualquer versão de pacote específica no grafo de dependência.
No exemplo abaixo, o aplicativo depende diretamente do Pacote B com uma restrição de versão de >=2.0.0. O aplicativo também depende do Pacote A, que, por sua vez, também depende do Pacote B, mas com uma restrição de >=1.0.0. Como a dependência do Pacote B 2.0.0 é uma dependência direta do aplicativo no grafo, essa versão será utilizada.
Aviso
A regra de ganho de dependência direta pode resultar em um downgrade da versão do pacote, potencialmente quebrando outras dependências no grafo. Quando um pacote é rebaixado, o NuGet adiciona um aviso para alertar o usuário.
Essa regra também resulta em maior eficiência com um grafo de dependência grande. Quando uma dependência mais próxima no mesmo subgrafo tem uma versão mais alta do que outra, o NuGet ignora essa dependência e o NuGet também ignora todas as dependências restantes nesse branch do grafo.
No diagrama abaixo, por exemplo, como o Pacote C 2.0.0 é usado, o NuGet ignora quaisquer branches nesse subgrafo que se referem a uma versão anterior do Pacote C:
Por meio dessa regra, o NuGet tenta honrar a intenção do autor do pacote. No diagrama abaixo, o autor do Pacote A rebaixou explicitamente o Pacote C da versão 2.0.0 para a versão 1.0.0.
O proprietário do aplicativo pode optar por atualizar o Pacote C para uma versão superior à 2.0.0, portanto, não é possível fazer downgrade da versão para o Pacote C. Nesse caso, nenhum aviso é gerado.
Dependências de primo
Quando diferentes versões de pacotes são referenciadas em subgrafos distintos no grafo do aplicativo, o NuGet utiliza a versão mais baixa que satisfaz todos os requisitos de versão (de acordo com as regras de versão mais baixa aplicável e de versões flutuantes ). Na imagem abaixo, por exemplo, a versão 2.0.0 do Pacote B atende à outra restrição de >=1.0.0 e, portanto, é usada:
Observe que os pacotes não precisam estar na mesma distância para que a regra de dependências de primo seja aplicada. No diagrama abaixo, o Pacote D 2.0.0 é escolhido no subgrafo do Pacote C e o Pacote D 3.0.0 é escolhido no subgrafo do Pacote A. No subgrafo de aplicativo, não há dependência direta do Pacote D, portanto, a versão versão mais baixa aplicável regra é aplicada e a versão 3.0.0 é escolhida.
Em alguns casos, não é possível atender a todos os requisitos de versão. Conforme mostrado abaixo, se o Pacote A exigir exatamente o Pacote B 1.0.0 e o Pacote C exigir o Pacote B >=2.0.0, o NuGet não poderá resolver as dependências e fornecerá um erro.
Nessas situações, o consumidor de nível superior (o aplicativo ou pacote) deve adicionar sua própria dependência direta ao Pacote B para que a dependência Direta ganhe regra se aplica.
Intervalos de versão e versões de pré-lançamento com PackageReference
Não é incomum que um pacote tenha versões estáveis e de pré-lançamento disponíveis.
Ao resolver um grafo de dependência, o NuGet decide se deve considerar versões de pré-lançamento para um pacote com base em uma única regra: 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.
Na prática, sob a regra mais baixa aplicável, isso significa:
Intervalo de versão | Versões disponíveis | Versão selecionada |
---|---|---|
[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 | Nenhum, NU1103 é levantado. |
[1.0.0, 2.0.0-rc) | 1.2.0-beta.1, 2.0.0-beta.3 | 1.2.0-beta.1 |
Resolução de dependências com packages.config
Com packages.config
, as dependências de um projeto são gravadas em packages.config
como uma lista simples. Todas as dependências desses pacotes também são escritas na mesma lista. Quando os pacotes são instalados, o NuGet também pode modificar o arquivo .csproj
, app.config
, web.config
e outros arquivos individuais.
Com packages.config
, o NuGet tenta resolver conflitos de dependência durante a instalação de cada pacote individual. Ou seja, se o Pacote A estiver sendo instalado e depender do Pacote B e o Pacote B já estiver listado em packages.config
como uma dependência de outra coisa, o NuGet comparará as versões do Pacote B que estão sendo solicitadas e tentará encontrar uma versão que atenda a todas as restrições de versão. Especificamente, o NuGet seleciona a versão de principal.minor de inferior que satisfaz as dependências.
Por padrão, o NuGet 2.8 procura a versão de patch mais baixa (consulte notas de lançamento do NuGet 2.8). Você pode controlar essa configuração por meio do atributo DependencyVersion
em NuGet.Config
e a opção -DependencyVersion
na linha de comando.
O processo de packages.config
para resolver dependências fica complicado para gráficos de dependência maiores. Cada nova instalação de pacote requer uma passagem de todo o grafo e aumenta a chance de conflitos de versão. Quando ocorre um conflito, a instalação é interrompida, deixando o projeto em um estado indeterminado, especialmente com possíveis modificações no próprio arquivo de projeto. Isso não é um problema ao usar outros formatos de gerenciamento de pacotes.
Intervalos de versão e versões de pré-lançamento com packages.config
A regra packages.config não permite a combinação de dependências de versão estável e de versão pré-lançamento em um grafo.
Se uma dependência for expressa com um intervalo como [1.0.0, 2.0.0)
, os pacotes de pré-lançamento não serão permitidos no grafo.
Gerenciando ativos de dependência
Ao usar o formato PackageReference, você pode controlar quais ativos de dependências fluem para o projeto de nível superior. Para obter detalhes, consulte PackageReference.
Quando o projeto de nível superior é em si um pacote, você também tem controle sobre esse fluxo usando os atributos include
e exclude
com dependências listadas no arquivo .nuspec
. Consulte referência .nuspec: Dependências.
Excluindo referências
Há cenários em que assemblies com o mesmo nome podem ser referenciados mais de uma vez em um projeto, produzindo erros durante o design e a construção. Considere um projeto que contém uma versão personalizada do C.dll
e referencia o Pacote C que também contém C.dll
. Ao mesmo tempo, o projeto também depende do Pacote B, que também depende do Pacote C e do C.dll
. Como resultado, o NuGet não pode determinar qual C.dll
usar, mas você não pode simplesmente remover a dependência do projeto no Pacote C porque o Pacote B também depende dele.
Para resolver isso, você deve referenciar diretamente o C.dll
desejado (ou usar outro pacote que referencia o correto) e, em seguida, adicionar uma dependência ao Pacote C que exclua todos os seus recursos. Isso é feito da seguinte maneira, dependendo do formato de gerenciamento de pacote em uso:
PackageReference: adicione
ExcludeAssets="All"
na dependência:<PackageReference Include="PackageC" Version="1.0.0" ExcludeAssets="All" />
packages.config
: remova a referência ao PackageC do arquivo.csproj
para que ele faça referência apenas à versão deC.dll
desejada.
Atualizações de dependência durante a instalação do pacote
Se uma versão de dependência já estiver satisfeita, a dependência não será atualizada durante outras instalações de pacote. Por exemplo, considere o pacote A que depende do pacote B e especifica 1.0 para o número de versão. O repositório de origem contém as versões 1.0, 1.1 e 1.2 do pacote B. Se A estiver instalado em um projeto que já contém a versão B 1.0, a B 1.0 permanecerá em uso porque atenderá à restrição de versão. No entanto, se o pacote A tivesse solicitado a versão 1.1 ou superior da B, então B 1.2 seria instalado.
Resolvendo erros de pacote incompatíveis
Durante uma operação de restauração de pacote, você poderá ver o erro "Um ou mais pacotes não são compatíveis..." ou que um pacote "não é compatível" com a estrutura de destino do projeto.
Esse erro ocorre quando um ou mais dos pacotes referenciados em seu projeto não indicam que dão suporte à estrutura de destino do projeto; ou seja, o pacote não contém uma DLL adequada em sua pasta lib
para uma estrutura de destino compatível com o projeto. (Consulte estruturas de destino para obter uma lista.)
Por exemplo, se um projeto for destinado a netstandard1.6
e você tentar instalar um pacote que contenha DLLs apenas nas pastas lib\net20
e \lib\net45
, você verá mensagens como as seguintes para o pacote e possivelmente seus dependentes:
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'.
Para resolver incompatibilidades, siga um destes procedimentos:
- Redirecione seu projeto para uma estrutura compatível com os pacotes que você deseja usar.
- Contate o autor dos pacotes e trabalhe com eles para adicionar suporte à estrutura escolhida. Cada página de listagem de pacotes no nuget.org tem um link Contact Owners para essa finalidade.
Dica
solução alternativa: o NuGetSolver é uma extensão do Visual Studio desenvolvida pelo Microsoft DevLabs, projetada para ajudar a resolver conflitos de dependência. Ele automatiza o processo de identificação e endereçamento desses problemas. Para obter mais detalhes, visite a página NuGetSolver no Visual Studio Marketplace e adoraríamos ouvir seus comentários sobre sua experiência.