Como o NuGet resolve as dependências do pacote

Sempre que um pacote for instalado ou reinstalado, o que inclui o que está sendo instalado como parte de um processo de restauração, o NuGet também instala todos os pacotes adicionais dos quais o primeiro pacote depende.

Essas dependências imediatas também podem ter suas próprias dependências, que podem continuar em uma profundidade arbitrária. Isso produz o que é chamado de um grafo de dependência que descreve as relações entre os pacotes em todos os níveis.

Quando vários pacotes têm a mesma dependência, a mesma ID de pacote pode aparecer no grafo várias vezes, potencialmente com restrições de versão diferentes. No entanto, somente uma versão de determinado pacote pode ser usada em um projeto, por isso o NuGet precisa escolher qual versão é usada. O processo exato depende do formato de gerenciamento de pacote em uso.

Resolução de dependência com PackageReference

Ao instalar os 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 é chamado de restauração transitiva. Reinstalar ou restaurar pacotes é um processo de baixar os pacotes listados no grafo, resultando em builds mais rápidos e mais previsíveis.

Você também usufruir das 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 habilitar a funcionalidade de arquivo de bloqueio para garantir a repetibilidade.

Quando o processo de restauração do NuGet for executado antes de um build, ele resolverá as dependências primeiro na memória e, em seguida, gravará o grafo resultante em um arquivo chamado project.assets.json.

O arquivo de ativos está localizado em MSBuildProjectExtensionsPath, cujo padrão é a pasta 'obj' do projeto. O MSBuild lê este arquivo e converte-o em um conjunto de pastas em que as referências em potencial podem ser encontradas e as adiciona à á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. É listado por padrão em ambos .gitignore e .tfignore. Consulte Pacotes e controle do código-fonte.

Regras de resolução de dependência

A restauração transitiva aplica quatro regras principais para resolver as dependências: versão mais antiga aplicável, versões flutuantes, direct-dependency-wins e dependências primas.

Versão mais antiga aplicável

A regra de versão aplicáveis mais antiga restaura a versão mais antiga possível de um pacote, conforme definido por suas dependências. Ela também se aplica às dependências do aplicativo ou à biblioteca de classes, a menos que seja declarado como flutuante.

Na figura a seguir, por exemplo, 1.0-beta é considerado inferior a 1.0, por isso o NuGet escolhe a versão 1.0:

Choosing the lowest applicable version

Na figura a seguir, 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 antiga encontrada, neste caso 2.2:

Choosing the next lowest version available on the feed

Quando um aplicativo especifica um número de versão exata, como 1.2, que não está disponível no feed, o NuGet falhará com um erro ao tentar instalar ou restaurar o pacote:

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

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 mais recente do 6.0.x". 4.* significa "usar a versão mais recente do 4.x". O uso de uma versão flutuante reduz as alterações no arquivo de projeto, mantendo 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 recente de um pacote que corresponde ao padrão de versão, por exemplo, 6.0.* obtém a versão mais recente de um pacote que começa com 6.0:

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

Versão Versões presentes no servidor Resolução Motivo Observações
* 1.1.0
1.1.1
1.2.0
1.3.0-alpha
1.2.0 A versão estável mais recente.
1.1.* 1.1.0
1.1.1
1.1.2-alpha
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-alpha
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-alpha
1.1.2-beta
1.3.0-beta
1.1.2-beta A versão mais alta a respeitar o padrão, inclui 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

Observação

A resolução de versão flutuante não leva em conta se um pacote está ou não listado. A resolução da versão flutuante será resolvida localmente se as condições puderem ser satisfeitas com pacotes na pasta de pacotes global.

Dependência direta ganha

Quando o gráfico de pacote para 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 seria escolhida para esse subgráfico e o restante seria ignorado. Esse comportamento permite que um aplicativo substitua qualquer versão do pacote específico no grafo de dependência.

No exemplo abaixo, o aplicativo depende diretamente no 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 >= 1.0.0. Como a dependência do Pacote B 2.0 é uma dependência direta do aplicativo no gráfico, essa versão é usada:

Application using the Direct dependency wins rule

Aviso

A regra A dependência direta ganha pode resultar em um downgrade da versão do pacote, potencialmente interrompendo, dessa forma, outras dependências no gráfico. 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 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 outra, o NuGet ignora essa dependência e 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 quaisquer ramificações no gráfico que fazem referência a uma versão anterior do Pacote C:

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

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 downgrade explícito do Pacote C 2.0.0 para o Pacote C 1.0.0.

When a package author explicitly downgrades, NuGet honors that.

O proprietário do aplicativo pode optar por atualizar o Pacote C para uma versão superior à 2.0.0, portanto, não fará mais downgrade da versão para o Pacote C. Neste caso, nenhum aviso é gerado.

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

Dependências primo

Quando versões diferentes do pacote são referidas na mesma distância no gráfico do aplicativo, o NuGet usa a versão mais antiga que cumpre todos os requisitos de versão (assim como acontece com as regras versão mais antiga 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:

Resolving cousin dependencies using the lower version that satisfies all constraints

Observe que os pacotes não precisam estar na mesma distância para que a regra de dependências primas 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 do Pacote D, portanto, a regra de versão mais antiga aplicável é aplicada e a versão 3.0.0 é escolhida.

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

Em alguns casos, não é possível atender a todos os requisitos de versão. Conforme mostrado abaixo, se o Pacote A requer exatamente o Pacote B 1.0.0 e o Pacote C requer Pacote B >>= 2.0.0, o NuGet não poderá resolver as dependências e apresentará um erro.

Unresolvable dependencies due to an exact version requirement

Nessas situações, o consumidor de nível superior (o aplicativo ou pacote) deve adicionar sua própria dependência direta no Pacote B para que a regra A dependência direta vence se aplique.

Resolução de dependência com packages.config

Com o packages.config, as dependências do projeto são gravadas em packages.config como uma lista simples. Todas as dependências desses pacotes também são gravadas 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 este já está listado em packages.config como uma dependência de outra coisa, o NuGet compara as versões do Pacote B que está sendo solicitado e tenta localizar uma versão que atenda a todas as restrições de versão. Especificamente, o NuGet seleciona a versão principal.secundária mais antiga que satisfaça as dependências.

Por padrão, o NuGet 2.8 procura a versão de patch mais baixa (veja Notas de versão 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 packages.config para resolver as dependências fica complicado para grandes grafos de dependência. Cada nova instalação do pacote exige uma transversal de todo o grafo e aumenta as chances 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 para o arquivo de projeto. Isso não é um problema ao usar outros formatos de gerenciamento de pacote.

Gerenciamento de ativos de dependência

Ao usar o formato PackageReference, é possível controlar de quais ativos as dependências fluem no projeto de nível superior. Para obter detalhes, veja PackageReference.

Quando o projeto de nível superior for 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 de .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 de tempo de design e de tempo de build. 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. Por outro lado, 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 não é possível você apenas remover a dependência do projeto no pacote C porque o pacote B também depende dele.

Para resolver isso, você precisa referenciar diretamente o C.dll que você deseja (ou usar outro pacote que faz referência ao pacote correto) e, em seguida, adiciona uma dependência ao Pacote C que exclui todos os seus ativos. Isso é feito da seguinte maneira dependendo do formato de gerenciamento de pacote em uso:

  • PackageReference: adicionar ExcludeAssets="All" à dependência:

    <PackageReference Include="PackageC" Version="1.0.0" ExcludeAssets="All" />
    
  • packages.config: remova a referência a PackageC do arquivo .csproj de forma que ele faça referência somente à versão de C.dll que você deseja.

Atualizações de dependência durante a instalação do pacote

Se uma versão de dependência já for atendida, 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 1.0 do B, B 1.0 permanecerá em uso, pois atende à restrição de versão. No entanto, se o pacote A solicitava a versão 1.1 ou superior de B, B 1.2 seria instalado.

Resolvendo erros de pacote incompatível

Durante uma operação de restauração de pacote, você pode vir a encontrar o erro “Um ou mais pacotes não são compatíveis...” ou uma indicação de 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 indica que eles 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 que é compatível com o projeto. (Consulte Estruturas de destino para ver uma lista.)

Por exemplo, se um projeto utiliza netstandard1.6 e você tentar instalar um pacote que contém as DLLs somente nas pastas lib\net20 e \lib\net45, você verá mensagens com a seguinte 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 as incompatibilidades, siga um destes procedimentos:

  • Redirecione seu projeto para uma estrutura que é compatível com os pacotes que você deseja usar.
  • Entre em contato com o autor dos pacotes e trabalhe junto a eles para adicionar suporte à estrutura escolhida. Cada página de listagem de pacote no nuget.org tem um link Contatar os Proprietários para essa finalidade.

Dica

Solução alternativa: NuGetSolver é uma extensão do Visual Studio desenvolvida pela Microsoft DevLabs, criada para ajudar na resolução de conflitos de dependência. Ela automatiza o processo de identificação e solução desses problemas. Para obter mais detalhes, visite a página NuGetSolver no Visual Studio Marketplace. Gostaríamos muito de ouvir seus comentários sobre sua experiência.