SDKs de projeto do .NET

Os projetos do .NET Core e do .NET 5 e versões posteriores são associados a um SDK (kit de desenvolvimento de software). Cada SDK de projeto é um conjunto de destinos do MSBuild e tarefasassociadas responsáveis por compilar, empacotar e publicar código. Um projeto que faz referência a um SDK de projeto às vezes é conhecido como um projeto no estilo SDK.

SDKs disponíveis

Os seguintes SDKs estão disponíveis:

ID Descrição Repositório
Microsoft.NET.Sdk O SDK do .NET https://github.com/dotnet/sdk
Microsoft.NET.Sdk.Web O SDK da Web do .NET https://github.com/dotnet/sdk
Microsoft.NET.Sdk.BlazorWebAssembly O SDK BlazorWebAssembly do .NET
Microsoft.NET.Sdk.Razor O SDK do Razor do .NET
Microsoft.NET.Sdk.Worker O SDK do Serviço de Trabalho do .NET
Microsoft.NET.Sdk.WindowsDesktop O SDK de Desktop do .NET, que inclui WinForms (Windows Forms) e WPF (Windows Presentation Foundation).* https://github.com/dotnet/winforms e https://github.com/dotnet/wpf

O SDK do .NET é o SDK base do .NET. Os outros SDKs fazem referência ao SDK do .NET e os projetos associados aos outros SDKs têm todas as propriedades do SDK do .NET disponíveis para eles. O SDK da Web, por exemplo, depende do SDK do .NET e do SDK do Razor.

Você também pode criar seu próprio SDK que pode ser distribuído por meio do NuGet.

* Do .NET 5 em diante, os projetos Windows Forms e projetos do WPF devem especificar o SDK do .NET (Microsoft.NET.Sdk) em vez de Microsoft.NET.Sdk.WindowsDesktop. Para esses projetos, a configuração TargetFramework para net5.0-windows e UseWPF ou UseWindowsForms para true importará automaticamente o SDK de Desktop do Windows. Se o projeto for direcionado ao .NET 5 ou versão posterior e especificar o SDK Microsoft.NET.Sdk.WindowsDesktop, você receberá o aviso de compilação NETSDK1137.

Arquivos de projeto

Os projetos do .NET são baseados no formato MSBuild. Os arquivos de projeto, que têm extensões como .csproj para projetos C# e .fsproj para projetos F#, estão em formato XML. O elemento raiz de um arquivo de projeto MSBuild é o elemento Project. O elemento Project tem um atributo opcional Sdk que especifica qual SDK (e versão) usar. Para usar as ferramentas .NET e criar seu código, defina o Sdk atributo como uma das IDs na tabela SDKs disponíveis.

<Project Sdk="Microsoft.NET.Sdk">
  ...
</Project>

Para especificar um SDK proveniente do NuGet, inclua a versão no final do nome ou especifique o nome e a versão no arquivo global.json.

<Project Sdk="MSBuild.Sdk.Extras/2.0.54">
  ...
</Project>

Outra maneira de especificar o SDK é com o elemento Sdk de nível superior:

<Project>
  <Sdk Name="Microsoft.NET.Sdk" />
  ...
</Project>

Fazer referência a um SDK de uma dessas maneiras simplifica muito os arquivos de projeto para .NET. Ao avaliar o projeto, o MSBuild adiciona importações implícitas para Sdk.props na parte superior do arquivo de projeto e Sdk.targets na parte inferior.

<Project>
  <!-- Implicit top import -->
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
  ...
  <!-- Implicit bottom import -->
  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>

Dica

Em um computador Windows, os arquivos Sdk.props e Sdk.targets podem ser encontrados na pasta %ProgramFiles%\dotnet\sdk\[version]\Sdks\Microsoft.NET.Sdk\Sdk\Sdk.

Pré-processar o arquivo de projeto

Você pode ver o projeto totalmente expandido, pois o MSBuild o vê depois que o SDK e seus destinos são incluídos usando o comando dotnet msbuild -preprocess. A opção de pré-processamento do comando dotnet msbuild mostra quais arquivos são importados, as fontes e as contribuições deles para a compilação sem realmente criar o projeto.

Se o projeto tiver várias estruturas de destino, concentre os resultados do comando em apenas uma estrutura especificando-o como uma propriedade MSBuild. Por exemplo:

dotnet msbuild -property:TargetFramework=netcoreapp2.0 -preprocess:output.xml

Includes e excludes padrões

Os includes e excludes padrões para itens Compile, recursos inseridos e itens None são definidos no SDK. Ao contrário dos projetos não SDK do .NET Framework, você não precisa especificar esses itens no arquivo de projeto, pois os padrões abrangem os casos de uso mais comuns. Esse comportamento torna o arquivo de projeto menor e mais fácil de entender e editar manualmente, se necessário.

A seguinte tabela mostra qual elemento e quais globs são incluídos e excluídos do SDK do .NET:

Elemento Incluir glob Excluir glob Remover glob
Compile **/*.cs (ou outras extensões de linguagem) **/*.user; **/*.*proj; **/*.sln; **/*.vssscc N/D
EmbeddedResource **/*.resx **/*.user; **/*.*proj; **/*.sln; **/*.vssscc N/D
None **/* **/*.user; **/*.*proj; **/*.sln; **/*.vssscc **/*.cs; **/*.resx

Observação

As pastas ./bin e ./obj, representadas pelas propriedades MSBuild $(BaseOutputPath) e $(BaseIntermediateOutputPath), são excluídas dos globs por padrão. As exclusões são representadas pela propriedade DefaultItemExcludes.

O SDK do .NET Desktop tem mais inclusões e exclusões para WPF. Para obter mais informações, confira Includes e excludes padrão.

Erros de compilação

Se você definir explicitamente qualquer um desses itens no arquivo de projeto, provavelmente receberá um erro de compilação "NETSDK1022" semelhante ao seguinte:

Itens "Compile" duplicados foram incluídos. O SDK do .NET inclui itens "Compile" do diretório do projeto por padrão. É possível remover esses itens do arquivo de projeto ou definir a propriedade "EnableDefaultCompileItems" como "false" se desejar incluí-los explicitamente no arquivo de projeto.

Itens "EmbeddedResource" duplicados foram incluídos. O SDK do .NET inclui itens "EmbeddedResource" do diretório do projeto por padrão. É possível remover esses itens do arquivo de projeto ou definir a propriedade "EnableDefaultEmbeddedResourceItems" como "false" se desejar incluí-los explicitamente no arquivo de projeto.

Para resolver os erros, siga um destes procedimentos:

  • Remova os itens explícitos Compile, EmbeddedResource ou None que correspondam aos implícitos listados na tabela anterior.

  • Defina a propriedade EnableDefaultItems para false a fim de desabilitar toda inclusão implícita de arquivos:

    <PropertyGroup>
      <EnableDefaultItems>false</EnableDefaultItems>
    </PropertyGroup>
    

    Se você quiser especificar arquivos a serem publicados com seu aplicativo, ainda poderá usar os mecanismos MSBuild conhecidos para isso, por exemplo, o elemento Content.

  • Desabilite seletivamente somente os globs Compile, EmbeddedResource ou None definindo as propriedades EnableDefaultCompileItems, EnableDefaultEmbeddedResourceItems ou EnableDefaultNoneItems como false:

    <PropertyGroup>
      <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
      <EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
      <EnableDefaultNoneItems>false</EnableDefaultNoneItems>
    </PropertyGroup>
    

    Se você desabilitar apenas o globs Compile, o Gerenciador de Soluções do Visual Studio ainda vai mostrar itens *.cs como parte do projeto, incluídos como itens None. Para desabilitar o glob implícito None, defina EnableDefaultNoneItems como false também.

Diretivas using implícitas

Do .NET 6 em diante, diretivas global using implícitas são adicionadas a novos projetos em C#. Isso significa que você pode usar tipos definidos nesses namespaces sem precisar especificar seu nome totalmente qualificado ou adicionar manualmente uma diretiva using. O aspecto implícito refere-se ao fato de que as diretivas global using são adicionadas a um arquivo gerado no diretório obj do projeto.

Diretivas implícitas global using são adicionadas para projetos que usam um dos seguintes SDKs:

  • Microsoft.NET.Sdk
  • Microsoft.NET.Sdk.Web
  • Microsoft.NET.Sdk.Worker
  • Microsoft.NET.Sdk.WindowsDesktop

Uma global using diretiva é adicionada para cada namespace em um conjunto de namespaces padrão baseados no SDK do projeto. Esses namespaces padrão são mostrados na tabela a seguir.

. Namespaces padrão
Microsoft.NET.Sdk System
System.Collections.Generic
System.IO
System.Linq
System.Net.Http
System.Threading
System.Threading.Tasks
Microsoft.NET.Sdk.Web System.Net.Http.Json
Microsoft.AspNetCore.Builder
Microsoft.AspNetCore.Hosting
Microsoft.AspNetCore.Http
Microsoft.AspNetCore.Routing
Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging
Microsoft.NET.Sdk.Worker Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging
Microsoft.NET.Sdk.WindowsDesktop (Windows Forms) Microsoft.NET.Sdk namespaces
System.Drawing
System.Windows.Forms
Microsoft.NET.Sdk.WindowsDesktop (WPF) Microsoft.NET.Sdk namespaces
Removido System.IO
Removido System.Net.Http

Se você quiser desabilitar esse recurso ou se quiser habilitar diretivas implícitas global using em um projeto C# existente, poderá fazê-lo por meio da propriedade MSBuild ImplicitUsings.

Você pode especificar mais diretivas implícitas global using adicionando itens Using (ou itens Import para projetos do Visual Basic) ao arquivo do projeto, por exemplo:

<ItemGroup>
  <Using Include="System.IO.Pipes" />
</ItemGroup>

Referências de pacote implícitas

Ao adotar como destino o .NET Core 1.0 – 2.2 ou o .NET Standard 1.0 – 2.0, o SDK do .NET adiciona referências implícitas a determinados metapacotes. Um metapacote é um pacote baseado em estrutura que consiste apenas em dependências em outros pacotes. Os metapacotes são referenciados implicitamente com base nas estruturas de destino especificadas na propriedade TargetFramework ou TargetFrameworks do arquivo de projeto.

<PropertyGroup>
  <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<PropertyGroup>
  <TargetFrameworks>netcoreapp2.1;net462</TargetFrameworks>
</PropertyGroup>

Se necessário, você pode desabilitar referências implícitas de pacote usando a propriedade DisableImplicitFrameworkReferences e adicionar referências explícitas apenas às estruturas ou pacotes necessários.

Recomendações:

  • Ao direcionar .NET Framework, o .NET Core 1.0 - 2.2 ou o .NET Standard 1.0 – 2.0, não adicione uma referência explícita aos metapacotes Microsoft.NETCore.App ou NETStandard.Library por meio de um item <PackageReference> no arquivo de projeto. Para projetos do .NET Core 1.0 – 2.2 e .NET Standard 1.0 – 2.0, esses metapacotes são implicitamente referenciados. Para projetos do .NET Framework, se qualquer versão de NETStandard.Library for necessária ao usar um pacote NuGet baseado em .NET Standard, o NuGet instalará automaticamente essa versão.
  • Se precisar de uma versão específica do runtime ao direcionar o .NET Core 1.0 - 2.2, use a propriedade <RuntimeFrameworkVersion> no seu projeto (por exemplo, 1.0.4) em vez de referenciar o metapacote. Por exemplo, talvez você precise de uma versão de patch específica do runtime 1.0.0 LTS se estiver usando implantações autossuficientes.
  • Se precisar de uma versão específica do metapacote NETStandard.Library ao ter como destino o .NET Standard 1.0 - 2.0, use a propriedade <NetStandardImplicitPackageVersion> e defina a versão necessária.

Eventos de build

Em projetos no estilo SDK, use um destino MSBuild chamado PreBuild ou PostBuild e defina a propriedade BeforeTargets como PreBuild ou a propriedade AfterTargets para PostBuild.

<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="&quot;$(ProjectDir)PreBuildEvent.bat&quot; &quot;$(ProjectDir)..\&quot; &quot;$(ProjectDir)&quot; &quot;$(TargetDir)&quot;" />
</Target>

<Target Name="PostBuild" AfterTargets="PostBuildEvent">
   <Exec Command="echo Output written to $(TargetDir)" />
</Target>

Observação

  • Você pode usar qualquer nome para os destinos do MSBuild. No entanto, o IDE do Visual Studio reconhece os destinos PreBuild e PostBuild, portanto, usando esses nomes, você pode editar os comandos no IDE.
  • As propriedades PreBuildEvent e PostBuildEvent não são recomendadas em projetos no estilo SDK, pois macros como $(ProjectDir) não são resolvidas. Por exemplo, não há suporte para o seguinte código:
<PropertyGroup>
  <PreBuildEvent>"$(ProjectDir)PreBuildEvent.bat" "$(ProjectDir)..\" "$(ProjectDir)" "$(TargetDir)"</PreBuildEvent>
</PropertyGroup>

Personalizar a compilação

Há várias maneiras de personalizar um build. Talvez você queira substituir uma propriedade passando-a como um argumento para um comando msbuild ou dotnet. Você também pode adicionar a propriedade ao arquivo de projeto ou a um arquivo Directory.Build.props. Para obter uma lista de propriedades úteis para projetos .NET, confira Referência do MSBuild para projetos do SDK do .NET.

Destinos personalizados

Projetos do .NET podem empacotar destinos e propriedades personalizadas do MSBuild para uso por projetos que consomem o pacote. Use esse tipo de extensibilidade quando quiser:

  • Estenda o processo de compilação.
  • Acesse artefatos do processo de compilação, como arquivos gerados.
  • Inspecione a configuração na qual o compilação é invocado.

Você adiciona destinos ou propriedades de compilação personalizados colocando arquivos no formulário <package_id>.targets ou <package_id>.props (por exemplo, Contoso.Utility.UsefulStuff.targets) na pasta de compilação do projeto.

O XML a seguir é um snippet de um arquivo .csproj que instrui o comando dotnet pack sobre o que empacotar. O elemento <ItemGroup Label="dotnet pack instructions"> coloca os arquivos de destino na pasta de compilação dentro do pacote. O elemento <Target Name="CollectRuntimeOutputs" BeforeTargets="_GetPackageFiles"> coloca os assemblies e arquivos .json na pasta de compilação.

<Project Sdk="Microsoft.NET.Sdk">

  ...
  <ItemGroup Label="dotnet pack instructions">
    <Content Include="build\*.targets">
      <Pack>true</Pack>
      <PackagePath>build\</PackagePath>
    </Content>
  </ItemGroup>
  <Target Name="CollectRuntimeOutputs" BeforeTargets="_GetPackageFiles">
    <!-- Collect these items inside a target that runs after build but before packaging. -->
    <ItemGroup>
      <Content Include="$(OutputPath)\*.dll;$(OutputPath)\*.json">
        <Pack>true</Pack>
        <PackagePath>build\</PackagePath>
      </Content>
    </ItemGroup>
  </Target>
  ...

</Project>

Para consumir um destino personalizado em seu projeto, adicione um elemento PackageReference que aponte para o pacote e sua versão. Ao contrário das ferramentas, o pacote de destinos personalizados está incluído no fechamento de dependência do projeto de consumo.

Você pode configurar como usar o destino personalizado. Como ele é um destino MSBuild, ele pode depender de um determinado destino, executar após outro destino ou ser invocado manualmente usando o comando dotnet msbuild -t:<target-name>. No entanto, para fornecer uma melhor experiência do usuário, você pode combinar ferramentas por projeto e destinos personalizados. Nesse cenário, a ferramenta por projeto aceita todos os parâmetros necessários e os converte na invocação dotnet msbuild necessária que executa o destino. Veja um exemplo desse tipo de sinergia no repositório Exemplos do MVP Summit 2016 Hackathon do projeto dotnet-packer.

Confira também