Como o NuGet resolve dependências de pacotes
Sempre que um pacote é instalado ou reinstalado, o que inclui ser instalado como parte de um processo de de restauração de, o NuGet também instala quaisquer pacotes adicionais dos quais esse primeiro pacote dependa.
Essas dependências imediatas podem então também ter dependências próprias, que podem continuar a uma profundidade arbitrária. Isso produz o que é chamado de gráfico 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, o mesmo ID de pacote pode aparecer no gráfico 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 será usada. O processo exato depende do formato de gerenciamento de pacotes 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 gráfico de pacote simples no arquivo apropriado e resolve conflitos com antecedência. Esse processo é conhecido como restauração transitiva. Reinstalar ou restaurar pacotes é então um processo de download dos pacotes listados no gráfico, resultando em compilações mais rápidas e previsíveis.
Você também pode aproveitar as versões flutuantes, como a 2.8.*, para evitar modificar o projeto para usar a versão mais recente de um pacote. Ao usar versões flutuantes, recomendamos ativar a funcionalidade de arquivo de bloqueio para garantir a repetibilidade.
Quando o processo de restauração do NuGet é executado antes de uma compilação, ele resolve as dependências primeiro na memória e, em seguida, grava o gráfico resultante em um arquivo chamado project.assets.json
.
O arquivo de ativos está localizado em MSBuildProjectExtensionsPath
, que assume como padrão a pasta 'obj' do projeto.
Em seguida, o MSBuild lê esse arquivo e o traduz em um conjunto de pastas onde possíveis referências podem ser encontradas e, em seguida, adiciona-as à árvore do 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 do código-fonte.
Regras de resolução de dependências
A restauração transitiva aplica quatro regras principais para resolver dependências: a versão aplicável mais baixa, versões flutuantes, vitória da dependência direta, 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, então o NuGet escolhe a versão 1.0:
Na figura seguinte, 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, neste caso a 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 "use 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 | Justificação | Observações |
---|---|---|---|---|
* | 1.1.0 1.1.1 1.2.0 1.3.0-alfa |
1.2.0 | A versão estável mais alta. | |
1.1.* | 1.1.0 1.1.1 1.1.2-alfa 1.2.0-alfa |
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, .NET Core SDK 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 respeitando o padrão e incluindo as versões não estáveis. | Disponível no Visual Studio versão 16.6, NuGet versão 5.6, .NET Core SDK 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 esta ser uma gama de versões que inclui uma parte de pré-lançamento, versões estáveis são permitidas se corresponderem à parte estável. Dado que é escolhido 1.2.0 > 1.2.0-rc.2. |
Observação
A resolução da versão flutuante não leva em conta se um pacote está listado ou não. A resolução da versão flutuante será resolvida localmente se as condições puderem ser satisfeitas com pacotes na Pasta Global de Pacotes.
Ganha a dependência direta
Quando o gráfico de pacote de um aplicativo contém versões diferentes de um pacote no mesmo subgráfico, e uma dessas versões é uma dependência direta nesse subgráfico, essa versão será escolhida para esse subgráfico e o restante será ignorado. Esse comportamento permite que um aplicativo substitua qualquer versão específica do pacote no gráfico 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 é dependência direta do aplicativo no gráfico, essa versão é usada:
Advertência
A regra "A dependência direta vence" pode resultar numa redução da versão do pacote, potencialmente rompendo outras dependências no grafo. Quando um pacote é rebaixado, o NuGet adiciona um aviso de para alertar o usuário.
Esta regra também resulta em maior eficiência com um grande gráfico de dependência. Quando uma dependência mais próxima no mesmo subgráfico tem uma versão mais alta do que uma outra, o NuGet ignora essa dependência e o NuGet também ignora todas as dependências restantes nessa ramificação do gráfico.
No diagrama abaixo, por exemplo, como o Pacote C 2.0.0 é usado, o NuGet ignora todas as ramificações nesse subgráfico 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 fez o rebaixamento explícito do Pacote C 2.0.0 para o Pacote C 1.0.0.
O proprietário do aplicativo pode optar por atualizar o Pacote C para uma versão superior a 2.0.0, portanto, não fazer mais downgrade da versão para o Pacote C. Neste caso, nenhum aviso é levantado.
Dependências de primos
Quando referidas diferentes versões de pacote em diferentes subgrafos no grafo da aplicação, o NuGet utiliza a versão mais baixa que satisfaz todos os requisitos de versão (como com as regras de versão mais baixa aplicável e versões flutuantes ). Na imagem abaixo, por exemplo, a versão 2.0.0 do Pacote B satisfaz a outra restrição >=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 primos seja aplicada. No diagrama abaixo, o Pacote D 2.0.0 é escolhido no subgráfico do Pacote C e o Pacote D 3.0.0 é escolhido no subgráfico do Pacote A. No subgráfico Aplicativo, não há dependência direta com o Pacote D, portanto, a regra de da versão mais baixa aplicável é aplicada e a versão 3.0.0 é escolhida.
Em alguns casos, não é possível atender a todos os requisitos de versão. Como mostrado abaixo, se o Pacote A requer exatamente o Pacote B 1.0.0 e o Pacote C requer o Pacote B >=2.0.0, o NuGet não pode resolver as dependências e fornece um erro.
Nessas situações, o consumidor superior (o aplicativo ou pacote) deve adicionar a sua própria dependência direta do Pacote B para que a regra de dependência direta ganhe se aplique.
Intervalos de versões 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 gráfico de dependência, o NuGet decide se considera 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, ao abrigo da regra mínima aplicável, isto significa:
Gama de versões | 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 | Nenhuma, a exceção NU1103 é levantada. |
[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ência com packages.config
Com packages.config
, as dependências de um projeto são gravadas em packages.config
como uma lista plana. Quaisquer 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 está sendo instalado e depende do Pacote B, e o Pacote B já está listado em packages.config
como uma dependência de outra coisa, o NuGet compara as versões do Pacote B que estão sendo solicitadas e tenta encontrar uma versão que satisfaça todas as restrições de versão. Especificamente, o NuGet seleciona a versão inferior major.minor que satisfaz as dependências.
Por padrão, o NuGet 2.8 procura a menor versão de correção possível (consulte notas de lançamento do NuGet 2.8). Você pode controlar essa configuração por meio do atributo DependencyVersion
no NuGet.Config
e da 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 travessia do gráfico completo e aumenta a probabilidade 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ões e versões de pré-lançamento com packages.config
A resolução packages.config não permite a mistura de dependências estáveis e de 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 gráfico.
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 é 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 num projeto, produzindo erros em tempo de design e compilação. Considere um projeto que contém uma versão personalizada do C.dll
e faz referência ao 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 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 do Pacote C porque o Pacote B também depende dele.
Para resolver isso, você deve fazer referência direta ao C.dll
desejado (ou usar outro pacote que faça referência ao correto) e, em seguida, adicionar uma dependência no Pacote C que exclui todos os seus ativos. Isso é feito da seguinte forma, dependendo do formato de gerenciamento de pacotes em uso:
PackageReference: adicionar
ExcludeAssets="All"
na dependência:<PackageReference Include="PackageC" Version="1.0.0" ExcludeAssets="All" />
packages.config
: remova a referência a PackageC do arquivo.csproj
para que ele faça referência apenas à versão doC.dll
que você deseja.
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 da 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 B versão 1.0, B 1.0 permanecerá em uso porque satisfaz a restrição de versão. No entanto, se o pacote A tivesse solicitações versão 1.1 ou superior do B, o B 1.2 seria instalado.
Resolução de erros de pacotes incompatíveis
Durante uma operação de restauração de pacote, você pode 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.
Este erro ocorre quando um ou mais dos pacotes referenciados no seu projeto não indicam que suportam a 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 que é compatível com o projeto. (Consulte Estruturas de destino para obter uma lista.)
Por exemplo, se um projeto tem como alvo netstandard1.6
e você tenta instalar um pacote que contém 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 o seu projeto para uma estrutura suportada pelos pacotes que pretende utilizar.
- Entre em contato com o autor dos pacotes e trabalhe com ele para adicionar suporte à estrutura escolhida. Cada página de listagem de pacotes no nuget.org tem um link Contactar os Proprietários para essa finalidade.
Dica
Solução alternativa: NuGetSolver é uma extensão do Visual Studio desenvolvida pela Microsoft DevLabs, projetada para ajudar na resolução de conflitos de dependência. Automatiza o processo de identificação e abordagem destas questões. Para obter mais detalhes, visite a página NuGetSolver no Visual Studio Marketplace e adoraríamos ouvir seus comentários sobre sua experiência.