PackageReference em arquivos de projeto

As referências de pacote, usando itens <PackageReference> do MSBuild especificam as dependências de pacotes do NuGet diretamente nos arquivos de projeto (em vez de um arquivo packages.config separado). O uso de PackageReference, não afeta outros aspectos do NuGet; por exemplo, as configurações nos arquivos NuGet.Config (incluindo as origens do pacote) ainda são aplicadas, conforme explicado em Configurações comuns do NuGet.

Com o PackageReference, você também pode usar condições do MSBuild para escolher as referências de pacote por estrutura de destino ou por outros agrupamentos. Ele também proporciona um controle refinado sobre as dependências e o fluxo de conteúdo. (Para obter mais detalhes, veja Empacotamento e restauração do NuGet como destinos do MSBuild.)

Suporte do tipo de projeto

Por padrão, o PackageReference é usado para projetos do .NET Core, para projetos do .NET Standard e para projetos da UWP direcionados ao Windows 10 Build 15063 (Atualização para Criadores) e posterior, com exceção dos projetos da C ++ UWP. Os projetos do .NET Framework são compatíveis com o PackageReference, mas, no momento, contam com packages.config como padrão. Para usar PackageReference, migre as dependências de packages.config para o arquivo de projeto e remova packages.config.

Os aplicativos ASP.NET do .NET Framework completo incluem apenas suporte limitado para PackageReference. Não há suporte para tipos de projeto C++ e JavaScript.

Adicionar um PackageReference

Adicione uma dependência ao arquivo de projeto usando a seguinte sintaxe:

<ItemGroup>
    <!-- ... -->
    <PackageReference Include="Contoso.Utility.UsefulStuff" Version="3.6.0" />
    <!-- ... -->
</ItemGroup>

Controlar a versão de dependência

A convenção para especificar a versão de um pacote é a mesma que usar packages.config:

<ItemGroup>
    <!-- ... -->
    <PackageReference Include="Contoso.Utility.UsefulStuff" Version="3.6.0" />
    <!-- ... -->
</ItemGroup>

No exemplo acima, 3.6.0 significa qualquer versão que é >=3.6.0 com preferência para a versão mais antiga, conforme descrito em Controle de versão de pacotes.

Usando PackageReference para um projeto sem dependências de pacotes

Avançado: se você não tem pacotes instalados em um projeto (não tem PackageReferences no arquivo de projeto nem um arquivo packages.config), mas deseja que o projeto seja restaurado como o estilo PackageReference, você pode definir uma propriedade de projeto RestoreProjectStyle como PackageReference em seu arquivo de projeto.

<PropertyGroup>
    <!--- ... -->
    <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
    <!--- ... -->
</PropertyGroup>    

Isso poderá ser útil se você fizer referência a projetos que são do estilo PackageReference (projetos de estilo csproj ou SDK existentes). Isso permitirá que os pacotes aos quais esses projetos fazem referência sejam referenciados "transitivamente" pelo seu projeto.

PackageReference e origens

Em projetos PackageReference, as versões de dependência transitiva são resolvidas no momento da restauração. Como tal, em projetos PackageReference todas as origens precisam estar disponíveis para todas as restaurações.

Versões flutuantes

Versões flutuante são compatíveis com PackageReference:

<ItemGroup>
    <!-- ... -->
    <PackageReference Include="Contoso.Utility.UsefulStuff" Version="3.6.*" />
    <PackageReference Include="Contoso.Utility.UsefulStuff" Version="3.6.0-beta.*" />
    <!-- ... -->
</ItemGroup>

Controlar ativos de dependência

Você pode estar usando uma dependência puramente como uma estrutura de desenvolvimento e pode não querer expor isso aos projetos que consumirão o pacote. Nesse cenário, você pode usar os metadados do PrivateAssets para controlar esse comportamento.

<ItemGroup>
    <!-- ... -->

    <PackageReference Include="Contoso.Utility.UsefulStuff" Version="3.6.0">
        <PrivateAssets>all</PrivateAssets>
    </PackageReference>

    <!-- ... -->
</ItemGroup>

As seguintes marcas de metadados controlam ativos de dependência:

Marca Descrição Valor Padrão
IncludeAssets Esses ativos serão consumidos all
ExcludeAssets Esses ativos não serão consumidos nenhum
PrivateAssets Esses ativos serão consumidos, mas não fluem para o projeto pai contentfiles;analyzers;build

Os valores permitidos para essas marcas são os seguintes, com vários valores separados por ponto e vírgula, exceto com all e none, que devem aparecer sozinhos:

Valor Descrição
compile Conteúdo da pasta lib e controles se seu projeto puder compilar em relação aos assemblies dentro da pasta
runtime Conteúdo da pasta lib e runtimes pasta e controles se esses assemblies forem copiados para o diretório de saída de compilação
contentFiles O conteúdo da pasta contentfiles
compilar .props e .targets na pasta build
buildMultitargeting (4.0) .props e .targets na pasta buildMultitargeting, para direcionamento entre estruturas
buildTransitive (5.0+) .props e .targets na pasta buildTransitive, para ativos que fluem transitivamente para qualquer projeto de consumo. Confira a página de recursos.
analyzers Analisadores de .NET
nativa O conteúdo da pasta native
nenhum Nenhuma das opções acima é usada.
all Todas as anteriores (exceto none)
<ItemGroup>
    <!-- ... -->
    <!-- Everything except the content files will be consumed by the project -->
    <!-- Everything except content files and analyzers will flow to the parent project-->
    <PackageReference Include="Contoso.Utility.UsefulStuff" Version="3.6.0">
        <IncludeAssets>all</IncludeAssets> <!-- Default is `all`, can be omitted-->
        <ExcludeAssets>contentFiles</ExcludeAssets>
        <PrivateAssets>contentFiles;analyzers</PrivateAssets>
    </PackageReference>
    <!-- ... -->
    <!-- Everything except the compile will be consumed by the project -->
    <!-- Everything except contentFiles will flow to the parent project-->
    <PackageReference Include="Contoso.Utility.SomeOtherUsefulStuff" Version="3.6.0">
        <ExcludeAssets>compile</ExcludeAssets>
        <PrivateAssets>contentFiles</PrivateAssets>
    </PackageReference>
    <!-- ... -->
</ItemGroup>

Observe que, como build não está incluído em PrivateAssets, destino e objetos fluirão para o projeto pai. Considere, por exemplo, que a referência acima é usada em um projeto que compila um pacote do NuGet chamado AppLogger. O AppLogger pode consumir os destinos e objetos de Contoso.Utility.UsefulStuff, bem como projetos que consomem AppLogger.

Observação

Quando developmentDependency é definido como true em um arquivo .nuspec, isso marca um pacote como uma dependência somente de desenvolvimento, o que impede que o pacote seja incluído como uma dependência em outros pacotes. Com PackageReference (NuGet 4.8+), esse sinalizador também significa que ele excluirá os recursos em tempo de compilação. Para obter mais informações, confira Suporte do DevelopmentDependency para PackageReference.

Adicionar uma condição de PackageReference

É possível usar uma condição para controlar se um pacote está incluído, em que as condições podem usar qualquer variável MSBuild ou uma variável definida no arquivo de destinos ou objetos. No entanto, no momento, somente a variável TargetFramework é compatível.

Por exemplo, digamos que seu destino é netstandard1.4 e net452, mas tem uma dependência que é aplicável somente para net452. Nesse caso, não é aconselhável ter um projeto netstandard1.4 que está consumindo seu pacote para adicionar essa dependência desnecessária. Para evitar isso, você deve especificar uma condição no PackageReference da seguinte maneira:

<ItemGroup>
    <!-- ... -->
    <PackageReference Include="Newtonsoft.Json" Version="9.0.1" Condition="'$(TargetFramework)' == 'net452'" />
    <!-- ... -->
</ItemGroup>

Um pacote compilado usando este projeto mostrará que Newtonsoft.Json está incluído como uma dependência somente para um destino net452:

O resultado da aplicação de uma condição em PackageReference com o VS2017

As condições também podem ser aplicadas no nível de ItemGroup e serão aplicadas a todos os elementos PackageReference filhos:

<ItemGroup Condition = "'$(TargetFramework)' == 'net452'">
    <!-- ... -->
    <PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
    <PackageReference Include="Contoso.Utility.UsefulStuff" Version="3.6.0" />
    <!-- ... -->
</ItemGroup>

GeneratePathProperty

Esse recurso está disponível no NuGet 5.0 ou superior e no Visual Studio 2019 16.0 ou superior.

Às vezes, é desejável fazer referência a arquivos em um pacote de um destino do MSBuild. Em projetos baseados em packages.config, os pacotes são instalados em uma pasta relativa ao arquivo de projeto. No entanto, em PackageReference, os pacotes são consumidos da pasta global-packages, que pode variar de máquina para máquina.

Para preencher essa lacuna, o NuGet introduziu uma propriedade que aponta para o local de onde o pacote será consumido.

Exemplo:

  <ItemGroup>
      <PackageReference Include="Some.Package" Version="1.0.0" GeneratePathProperty="true" />
  </ItemGroup>

  <Target Name="TakeAction" AfterTargets="Build">
    <Exec Command="$(PkgSome_Package)\something.exe" />
  </Target>

Além disso, o NuGet gerará automaticamente propriedades para pacotes que contêm uma pasta de ferramentas.

  <ItemGroup>
      <PackageReference Include="Package.With.Tools" Version="1.0.0" />
  </ItemGroup>

  <Target Name="TakeAction" AfterTargets="Build">
    <Exec Command="$(PkgPackage_With_Tools)\tools\tool.exe" />
  </Target>

As propriedades e identidades de pacote do MSBuild não têm as mesmas restrições, portanto, a identidade do pacote precisa ser alterada para um nome amigável do MSBuild, prefixado pela palavra Pkg. Para verificar o nome exato da propriedade gerada, examine o arquivo nuget.g.props gerado.

Aliases de PackageReference

Em alguns casos raros, pacotes diferentes conterão classes no mesmo namespace. A partir do NuGet 5.7 e Visual Studio 2019 Atualização 7, equivalente ao ProjectReference, o PackageReference é compatível com o Aliases. Por padrão, nenhum alias é fornecido. Quando um alias é especificado, todos os assemblies provenientes do pacote anotado precisam ser referenciados com um alias.

Você pode ver o uso de exemplo em NuGet\Samples

No arquivo de projeto, especifique os aliases da seguinte forma:

  <ItemGroup>
    <PackageReference Include="NuGet.Versioning" Version="5.8.0" Aliases="ExampleAlias" />
  </ItemGroup>

e, no código, use-o da seguinte forma:

extern alias ExampleAlias;

namespace PackageReferenceAliasesExample
{
...
        {
            var version = ExampleAlias.NuGet.Versioning.NuGetVersion.Parse("5.0.0");
            Console.WriteLine($"Version : {version}");
        }
...
}

Avisos e erros do NuGet

Esse recurso está disponível no NuGet 4.3 ou superior e no Visual Studio 2017 15.3 ou superior.

Para muitos cenários de empacotar e restaurar, todos os avisos e erros do NuGet são codificados e começam com NU****. Todos os avisos e erros do NuGet estão listados na documentação de referência.

O NuGet observa as seguintes propriedades de aviso:

  • TreatWarningsAsErrors, trata todos os avisos como erros
  • WarningsAsErrors, trata avisos específicos como erros
  • NoWarn, oculta avisos específicos, em todo o projeto ou em todo o pacote.

Exemplos:

<PropertyGroup>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
...
<PropertyGroup>
    <WarningsAsErrors>$(WarningsAsErrors);NU1603;NU1605</WarningsAsErrors>
</PropertyGroup>
...
<PropertyGroup>
    <NoWarn>$(NoWarn);NU5124</NoWarn>
</PropertyGroup>
...
<ItemGroup>
    <PackageReference Include="Contoso.Package" Version="1.0.0" NoWarn="NU1605" />
</ItemGroup>

Suprimindo avisos do NuGet

Embora seja recomendável que você resolva todos os avisos do NuGet durante as operações de empacotar e restaurar, em determinadas situações sua supressão é justificada. Para suprimir aviso em todo o projeto, considere fazer:

<PropertyGroup>
    <PackageVersion>5.0.0</PackageVersion>
    <NoWarn>$(NoWarn);NU5104</NoWarn>
</PropertyGroup>
<ItemGroup>
    <PackageReference Include="Contoso.Package" Version="1.0.0-beta.1"/>
</ItemGroup>

Às vezes, os avisos se aplicam apenas a um determinado pacote no gráfico. Podemos optar por suprimir esse aviso de forma mais seletiva adicionando um NoWarn no item PackageReference.

<PropertyGroup>
    <PackageVersion>5.0.0</PackageVersion>
</PropertyGroup>
<ItemGroup>
    <PackageReference Include="Contoso.Package" Version="1.0.0-beta.1" NoWarn="NU1603" />
</ItemGroup>

Suprimindo avisos de pacotes NuGet no Visual Studio

Quando no Visual Studio, você também pode suprimir avisos via IDE.

Bloqueio de dependências

Esse recurso está disponível no NuGet 4.9 ou superior e no Visual Studio 2017 15.9 ou superior.

A entrada da restauração do NuGet é um conjunto de itens PackageReference do arquivo de projeto (dependências de nível superior ou diretas), e a saída é um fechamento completo de todas as dependências do pacote, incluindo as dependências transitivas. Se a lista PackageReference de entrada não tiver sido alterada, o NuGet tenta sempre produzir o mesmo fechamento completo das dependências de pacotes. No entanto, em alguns cenários, isso não poderá ser feito. Por exemplo:

  • Quando você usa versões flutuantes como <PackageReference Include="My.Sample.Lib" Version="4.*"/>. Embora a intenção aqui seja derivar para a versão mais recente sempre que uma restauração de pacotes ocorrer, há cenários em que os usuários exigem que o grafo seja bloqueado em uma determinada versão mais recente e derive para uma versão posterior, se disponível, mediante um gesto explícito.

  • Uma versão mais recente do pacote que corresponde aos requisitos de versão do PackageReference é publicada. Por exemplo,

    • Dia 1: se você tiver especificado <PackageReference Include="My.Sample.Lib" Version="4.0.0"/>, mas as versões disponíveis nos repositórios do NuGet forem 4.1.0, 4.2.0 e 4.3.0. Nesse caso, o NuGet teria que ser resolvido como 4.1.0 (mais próximo da versão mínima)

    • Dia 2: a versão 4.0.0 é publicada. O NuGet agora encontrará a correspondência exata e iniciará a resolução como 4.0.0

  • Uma determinada versão de pacote é removida do repositório. Embora o nuget.org não permita exclusões de pacotes, nem todos os repositórios de pacotes possuem essa restrição. Isso faz com que o NuGet encontre a melhor correspondência quando não puder resolver a versão excluída.

Habilitando o arquivo de bloqueio

Para persistir o fechamento completo das dependências de pacotes, você pode optar pelo recurso de bloqueio de arquivos definindo a propriedade RestorePackagesWithLockFile do MSBuild para seu projeto:

<PropertyGroup>
    <!--- ... -->
    <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
    <!--- ... -->
</PropertyGroup>    

Se essa propriedade for definida, a restauração do NuGet gerará um arquivo de bloqueio – arquivo packages.lock.json no diretório raiz do projeto que lista todas as dependências do pacote.

Observação

Se um projeto tiver o arquivo packages.lock.json no diretório raiz, o arquivo de bloqueio sempre será usado com a restauração, mesmo que a propriedade RestorePackagesWithLockFile não esteja definida. Portanto, outra maneira de optar por esse recurso é através da criação um arquivo packages.lock.json fictício em branco no diretório raiz do projeto.

Comportamento restore com o arquivo de bloqueio

Se houver um arquivo de bloqueio para o projeto, o NuGet usará esse arquivo de bloqueio para executar restore. O NuGet faz uma verificação rápida para ver se houve alguma alteração nas dependências do pacote, conforme mencionado no arquivo do projeto (ou nos arquivos dependentes dos projetos) e, se não houver alterações, ele apenas restaurará os pacotes mencionados no arquivo de bloqueio. Não há reavaliações das dependências do pacote.

Se o NuGet detectar uma alteração nas dependências definidas, conforme mencionado nos arquivos de projeto, ele reavaliará o grafo do pacote e atualizará o arquivo de bloqueio para refletir o novo fechamento do pacote para o projeto.

Para CI/CD e outros cenários, em que você não deseja alterar as dependências de pacote rapidamente, é possível configurar o lockedmode como true:

Para dotnet.exe, execute:

> dotnet.exe restore --locked-mode

Para msbuild.exe, execute:

> msbuild.exe -t:restore -p:RestoreLockedMode=true

Você também pode definir essa propriedade condicional do MSBuild em seu arquivo de projeto:

<PropertyGroup>
    <!--- ... -->
    <RestoreLockedMode>true</RestoreLockedMode>
    <!--- ... -->
</PropertyGroup> 

Se o modo de bloqueio for true, a restauração ocorrerá nos pacotes exatos conforme listado no arquivo de bloqueio ou não ocorrerá se você tiver atualizado as dependências de pacote definidas para o projeto após a criação do arquivo de bloqueio.

Tornar o arquivo de bloqueio parte de seu repositório de origem

Se você estiver compilando um aplicativo, um executável e o projeto em questão estarão no início da cadeia da dependência, portanto, verifique o arquivo de bloqueio no repositório do código-fonte para que o NuGet possa utilizá-lo durante a restauração.

No entanto, se seu projeto for um projeto de biblioteca que não é enviado ou um projeto de código comum do qual outros projetos dependem, você não deverá fazer check-in do arquivo de bloqueio como parte de seu código-fonte. Não há mal nenhum em manter o arquivo de bloqueio. No entanto, as dependências do pacote bloqueado do projeto de código comum não podem ser usadas, conforme listado no arquivo de bloqueio, durante a restauração/compilação de um projeto que depende desse projeto de código comum.

Por exemplo:

ProjectA
  |------> PackageX 2.0.0
  |------> ProjectB
             |------>PackageX 1.0.0

Se ProjectA tiver uma dependência em uma versão 2.0.0 do PackageX, além de referências ProjectB que dependem da versão 1.0.0 do PackageX, o arquivo de bloqueio de ProjectB listará uma dependência na versão 1.0.0 do PackageX. No entanto, quando ProjectA for criado, seu arquivo de bloqueio conterá uma dependência na versão 2.0.0 do PackageX e não na versão 1.0.0, conforme listado no arquivo de bloqueio de ProjectB. Portanto, o arquivo de bloqueio de um projeto de código comum tem pouco a dizer sobre os pacotes resolvidos de projetos que dependem dele.

Extensibilidade do arquivo de bloqueio

Você pode controlar vários comportamentos de restauração com o arquivo de bloqueio conforme descrito abaixo:

Opção do NuGet.exe Opção do dotnet Opção equivalente do MSBuild Descrição
-UseLockFile --use-lock-file RestorePackagesWithLockFile Opta pelo uso de um arquivo de bloqueio.
-LockedMode --locked-mode RestoreLockedMode Habilita o modo de bloqueio para a restauração. Isso é útil em cenários de CI/CD em que você deseja builds repetidos.
-ForceEvaluate --force-evaluate RestoreForceEvaluate Esta opção é útil com pacotes que têm a versão flutuante definida no projeto. Por padrão, a restauração do NuGet não atualizará automaticamente a versão do pacote em cada restauração, a menos que você execute a restauração com esta opção.
-LockFilePath --lock-file-path NuGetLockFilePath Define o local de um arquivo de bloqueio personalizado para um projeto. Por padrão, o NuGet é compatível com packages.lock.json no diretório raiz. Se você tiver vários projetos no mesmo diretório, o NuGet oferecerá suporte ao arquivo de bloqueio packages.<project_name>.lock.json específico do projeto

Resolvedor de Dependência do NuGet

O resolvedor de dependência do NuGet segue as 4 regras, conforme descrito no documento de resolução de dependência.

Para melhorar o desempenho e a escalabilidade da operação de restauração, o algoritmo de restauração foi reescrito na versão 6.12. A partir da versão 6.12, o novo algoritmo de restauração é habilitado por padrão para todos os projetos PackageReference. Embora o novo algoritmo de restauração seja funcionalmente equivalente ao anterior, como acontece com qualquer software, bugs são possíveis. Para reverter para a implementação anterior, defina a propriedade MSBuild RestoreUseLegacyDependencyResolver como true.

Se você enfrentar falhas de restauração na versão 6.12, .NET 9 ou 17.12 que não estavam sendo reproduzidas em versões anteriores, registre um problema no GitHub. Quaisquer diferenças entre os algoritmos antigos e novos podem ter impactos diferentes, como durante a compilação ou no runtime. Também há a chance de que as alterações não levem a falhas, mas a diferentes versões de pacotes sendo restauradas. Se você acha que pode ser afetado por alguma alteração, aqui estão as etapas que você pode seguir para verificar se as alterações no algoritmo de restauração do NuGet são a causa raiz.

O Restore grava seus resultados no diretório MSBuildProjectExtensionsPath, que pode ser comparado com os algoritmos novos e antigos para achar diferenças. Normalmente, esta é a pasta obj da sua build. Você pode usar msbuild.exe ou dotnet.exe para as próximas etapas.

  1. Remova a pasta obj do seu projeto.
  2. Execute msbuild -t:restore
  3. Salve o conteúdo de obj em uma localização indicando que é o comportamento new.
  4. Execute msbuild -t:restore -p:RestoreUseLegacyDependencyResolver="true"
  5. Salve o conteúdo de obj em uma localização indicando que é o comportamento legacy.
  6. Compare os arquivos nos dois diretórios, particularmente project.assets.json. As ferramentas que podem realçar as diferenças são especialmente úteis para isso (por exemplo, Visual Studio Code, abra os dois arquivos e clique com o botão direito do mouse em "selecionar para comparar" e "comparar com selecionados")

Se você seguir o método acima, deve haver exatamente 1 diferença entre os arquivos project.assets.json:

      "projectStyle": "PackageReference",
+     "restoreUseLegacyDependencyResolver": true,
      "fallbackFolders": [

Se houver mais diferenças, registre um problema no GitHub com todos os detalhes.

AssetTargetFallback

A propriedade AssetTargetFallback permite especificar versões de estrutura compatíveis adicionais para projetos referenciados por seu projeto e pacotes NuGet consumidos pelo seu projeto.

Se você especificar uma dependência de pacote usando PackageReference, mas esse pacote não contiver ativos compatíveis com a estrutura de destino dos seus projetos, a propriedade AssetTargetFallback entrará em jogo. A compatibilidade do pacote referenciado é verificada novamente usando cada estrutura de destino especificada em AssetTargetFallback. Quando um project ou package for referenciado via AssetTargetFallback, o aviso NU1701 será gerado.

Consulte a tabela abaixo para obter exemplos comuns.

Estrutura do projeto AssetTargetFallback Estruturas de pacotes Resultado
.NET Framework 4.7.2 .NET Standard 2.0 .NET Standard 2.0
.NET Core App 3.1 .NET Standard 2.0, .NET Framework 4.7.2 .NET Standard 2.0
.NET Core App 3.1 .NET Framework 4.7.2 Incompatível, falha com NU1202
.NET Core App 3.1 net472; net471 .NET Framework 4.7.2 .NET Framework 4.7.2 com NU1701

Várias estruturas podem ser especificadas usando ; como delimitador. Para adicionar uma estrutura de fallback, você pode fazer o seguinte:

<AssetTargetFallback Condition=" '$(TargetFramework)'=='netcoreapp3.1' ">
    $(AssetTargetFallback);net472;net471
</AssetTargetFallback>

Você pode deixar $(AssetTargetFallback) de fora se desejar substituir, em vez de adicionar aos valores de AssetTargetFallback existentes.

Observação

Se você estiver usando um projeto baseado em .NET SDK, os valores $(AssetTargetFallback) apropriados serão configurados e você não precisará defini-los manualmente.

$(PackageTargetFallback) era um recurso anterior que tentava resolver esse desafio, mas está fundamentalmente quebrado e não deve ser usado. Para migrar de $(PackageTargetFallback) para $(AssetTargetFallback), basta alterar o nome da propriedade.