SDK per i progetti .NET

I progetti .NET Core e .NET 5 e versioni successive sono associati a un SDK (Software Development Kit). Ogni SDK di progetto è un set di destinazioni di MSBuild e attività associate responsabili della compilazione, della compressione e della pubblicazione del codice. Un progetto che fa riferimento a un SDK di progetto viene talvolta definito progetto in stile SDK.

SDK disponibili

Sono disponibili gli SDK seguenti:

ID Descrizione Repository
Microsoft.NET.Sdk .NET SDK https://github.com/dotnet/sdk
Microsoft.NET.Sdk.Web Web SDK di .NET https://github.com/dotnet/sdk
Microsoft.NET.Sdk.BlazorWebAssembly SDK Blazor WebAssembly di .NET https://github.com/dotnet/aspnetcore
Microsoft.NET.Sdk.Razor Razor SDK di .NET https://github.com/dotnet/aspnetcore
Microsoft.NET.Sdk.Worker SDK Worker Service di .NET
Microsoft.NET.Sdk.WindowsDesktop Desktop SDK di .NET , che include Windows Form (WinForms) e Windows Presentation Foundation (WPF).* https://github.com/dotnet/winforms e https://github.com/dotnet/wpf

.NET SDK è l'SDK di base per .NET. Gli altri SDK fanno riferimento a .NET SDK e tutte le proprietà di .NET SDK sono disponibili per i progetti associati agli altri SDK. Web SDK ad esempio dipende sia da .NET SDK sia da Razor SDK.

È anche possibile creare un SDK personalizzato che può essere distribuito tramite NuGet.

* A partire da .NET 5, i progetti Windows Form e Windows Presentation Foundation (WPF) devono specificare .NET SDK (Microsoft.NET.Sdk) anziché Microsoft.NET.Sdk.WindowsDesktop. Per questi progetti, l'impostazione di TargetFramework su net5.0-windows e UseWPF o UseWindowsForms su true importerà automaticamente Windows Desktop SDK. Se il progetto è destinato a .NET 5 o versione successiva e specifica Microsoft.NET.Sdk.WindowsDesktop SDK, verrà visualizzato un avviso di compilazione per NETSDK1137.

File di progetto

I progetti .NET sono basati sul formato MSBuild. I file di progetto, con estensioni come csproj per i progetti C# e fsproj per i progetti F#, sono in formato XML. L'elemento radice di un file di progetto MSBuild è l'elemento Project. L'elemento Project ha un attributo Sdk facoltativo che specifica quale SDK (e versione) usare. Per usare gli strumenti .NET e compilare il codice, impostare l'attributo Sdk su uno degli ID nella tabella SDK disponibili.

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

Per specificare un SDK proveniente da NuGet, includere la versione alla fine del nome o specificare il nome e la versione nel file global.json.

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

Un altro modo per specificare l'SDK consiste nell'usare l'elemento Sdk di primo livello:

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

Fare riferimento a un SDK in uno di questi modi semplifica notevolmente i file di progetto per .NET. Durante la valutazione del progetto, MSBuild aggiunge importazioni implicite per Sdk.props nella parte superiore del file di progetto e Sdk.targets nella parte inferiore.

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

Suggerimento

In un computer Windows i file Sdk.props e Sdk.targets sono disponibili nella cartella %Programmi%\dotnet\sdk\[version]\Sdks\Microsoft.NET.Sdk\Sdk.

Pre-elaborare il file di progetto

È possibile visualizzare il progetto completamente espanso, nel modo in cui viene visualizzato da MSBuild dopo che l'SDK e le relative destinazioni sono incluse usando il comando dotnet msbuild -preprocess. L'opzione preprocess del comando dotnet msbuild mostra quali file vengono importati, le rispettive origini e i relativi contributi alla build senza compilare effettivamente il progetto.

Se il progetto ha più framework di destinazione, concentrare i risultati del comando su un solo framework specificandolo come proprietà di MSBuild. Ad esempio:

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

Inclusioni ed esclusioni predefinite

Le inclusioni ed esclusioni predefinite per gli elementi Compile, le risorse incorporate e gli elementi None vengono definite nell'SDK. A differenza dei progetti .NET Framework non SDK, non è necessario specificare questi elementi nel file di progetto, perché le impostazioni predefinite coprono i casi d'uso più comuni. Questo comportamento rende il file di progetto più piccolo e più facile da comprendere e modificare manualmente, se necessario.

La tabella seguente mostra gli elementi e i glob inclusi ed esclusi nell'SDK:

Elemento GLOB Include GLOB Exclude GLOB Remove
Compile **/*.cs (o altre estensioni del linguaggio) **/*.user; **/*.*proj; **/*.sln; **/*.vssscc N/D
EmbeddedResource **/*.resx **/*.user; **/*.*proj; **/*.sln; **/*.vssscc N/D
None **/* **/*.user; **/*.*proj; **/*.sln; **/*.vssscc **/*.cs; **/*.resx

Nota

Le cartelle ./bin e ./obj, rappresentate dalle proprietà $(BaseOutputPath) e $(BaseIntermediateOutputPath) di MSBuild, vengono escluse dai glob per impostazione predefinita. Le esclusioni sono rappresentate dalla proprietà DefaultItemExcludes.

.NET Desktop SDK ha più inclusioni ed esclusioni per WPF. Per altre informazioni, vedere Inclusioni ed esclusioni predefinite per WPF.

Errori di compilazione

Se si definisce in modo esplicito uno di questi elementi nel file di progetto, è probabile che venga visualizzato un errore di compilazione "NETSDK1022" simile al seguente:

Sono stati inclusi elementi 'Compile' duplicati. .NET SDK include gli elementi 'Compile' dalla directory del progetto per impostazione predefinita. È possibile rimuovere questi elementi dal file di progetto oppure impostare la proprietà 'EnableDefaultCompileItems' su 'false' se si vuole includerli in modo esplicito nel file di progetto.

Sono stati inclusi elementi 'EmbeddedResource' duplicati. .NET SDK include gli elementi 'EmbeddedResource' dalla directory del progetto per impostazione predefinita. È possibile rimuovere questi elementi dal file di progetto oppure impostare la proprietà 'EnableDefaultEmbeddedResourceItems' su 'false' se si vuole includerli in modo esplicito nel file di progetto.

Per risolvere gli errori, eseguire una delle operazioni seguenti:

  • Rimuovere gli elementi espliciti Compile, EmbeddedResource o None corrispondenti a quelli impliciti elencati nella tabella precedente.

  • Impostare la proprietà EnableDefaultItems su false per disabilitare l'inclusione implicita di tutti i file:

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

    Se si vogliono specificare i file da pubblicare con l'app, è comunque possibile usare i meccanismi di MSBuild noti a tale scopo, ad esempio l'elemento Content.

  • Disabilitare in modo selettivo solo i glob Compile, EmbeddedResource o None impostando la proprietà EnableDefaultCompileItems, EnableDefaultEmbeddedResourceItems o EnableDefaultNoneItems su false:

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

    Se si disabilitano solo i glob Compile, Esplora soluzioni in Visual Studio visualizza ancora elementi *.cs come parte del progetto, inclusi come elementi None. Per disabilitare il glob None implicito, impostare anche EnableDefaultNoneItems su false.

Direttive using implicite

A partire da .NET 6, le direttive global using implicite vengono aggiunte ai nuovi progetti C#. Ciò significa che è possibile usare i tipi definiti in questi spazi dei nomi senza dover specificare il nome completo o aggiungere manualmente una direttiva using. L'aspetto implicito fa riferimento al fatto che le direttive global using vengono aggiunte a un file generato nella directory obj del progetto.

Vengono aggiunte direttive global using implicite per i progetti che usano uno degli SDK seguenti:

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

Viene aggiunta una direttiva global using per ogni spazio dei nomi in un set di spazi dei nomi predefiniti basati sull'SDK di progetto. Questi spazi dei nomi predefiniti sono illustrati nella tabella seguente.

SDK Spazi dei nomi predefiniti
Microsoft.NET.Sdk System
System.Collections.Generic
System.IO
System.Linq
System.Net.Http
System.Threading
System.Threading.Tasks
Microsoft.NET.Sdk.Web Spazi dei nomi Microsoft.NET.Sdk
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 Spazi dei nomi Microsoft.NET.Sdk
Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging
Microsoft.NET.Sdk.WindowsDesktop (Windows Forms) Spazi dei nomi Microsoft.NET.Sdk
System.Drawing
System.Windows.Forms
Microsoft.NET.Sdk.WindowsDesktop (WPF) Spazi dei nomi Microsoft.NET.Sdk
System.IO rimosso
System.Net.Http rimosso

Se si vuole disabilitare questa funzionalità o se si vogliono abilitare direttive global using implicite in un progetto C# esistente, è possibile farlo tramite la proprietà ImplicitUsings di MSBuild.

È possibile specificare ulteriori direttive global using implicite aggiungendo elementi Using o Import elementi per i progetti di Visual Basic al file di progetto, ad esempio:

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

Riferimenti impliciti al pacchetto

Quando la destinazione è .NET Core 1.0 - 2.2 o .NET Standard 1.0 - 2.0, .NET SDK aggiunge riferimenti impliciti a determinati metapacchetti. Un metapacchetto è un pacchetto basato su framework costituito solo da dipendenze da altri pacchetti. È possibile fare riferimento ai metapacchetti in modo implicito in base ai framework di destinazione specificati nella proprietà TargetFramework o TargetFrameworks del file di progetto.

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

Se necessario, è possibile disabilitare i riferimenti impliciti ai pacchetti usando la proprietà DisableImplicitFrameworkReferences e aggiungere riferimenti espliciti solo ai framework o ai pacchetti necessari.

Raccomandazioni:

  • Quando la destinazione è .NET Framework, .NET Core 1.0 - 2.2 o .NET Standard 1.0 - 2.0, non aggiungere un riferimento esplicito ai metapacchetti Microsoft.NETCore.App o NETStandard.Library tramite un elemento <PackageReference> nel file di progetto. Per i progetti .NET Core 1.0 - 2.2 e .NET Standard 1.0 - 2.0, viene fatto riferimento in modo implicito a questi metapacchetti. Per i progetti .NET Framework, se è necessaria una versione di NETStandard.Library quando si usa un pacchetto NuGet basato su .NET Standard, NuGet installa automaticamente tale versione.
  • Se è necessaria una versione specifica del runtime quando la destinazione è .NET Core 1.0 - 2.2, usare la proprietà <RuntimeFrameworkVersion> nel progetto (ad esempio, 1.0.4) anziché fare riferimento al metapacchetto. Ad esempio, potrebbe essere necessaria una versione della patch specifica del runtime 1.0.0 LTS se si usano distribuzioni autonome.
  • Se è necessaria una versione specifica del metapacchetto NETStandard.Library quando la destinazione è .NET Standard 1.0 - 2.0, è possibile usare la proprietà <NetStandardImplicitPackageVersion> e impostare la versione necessaria.

Eventi di compilazione

Nei progetti in stile SDK usare una destinazione MSBuild denominata PreBuild o PostBuild e impostare la proprietà BeforeTargets per PreBuild o la proprietà AfterTargets per 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>

Nota

  • È possibile usare qualsiasi nome per le destinazioni MSBuild. L'IDE di Visual Studio riconosce tuttavia le destinazioni PreBuild e PostBuild, quindi usando tali nomi è possibile modificare i comandi nell'IDE.
  • Le proprietà PreBuildEvent e PostBuildEvent non sono consigliate nei progetti in stile SDK, perché le macro come $(ProjectDir) non vengono risolte. Ad esempio, il codice seguente non è supportato:
<PropertyGroup>
  <PreBuildEvent>"$(ProjectDir)PreBuildEvent.bat" "$(ProjectDir)..\" "$(ProjectDir)" "$(TargetDir)"</PreBuildEvent>
</PropertyGroup>

Personalizzare la compilazione

Esistono diversi modi per personalizzare una compilazione. È possibile eseguire l'override di una proprietà passandola come argomento a un comando msbuild o dotnet. È anche possibile aggiungere la proprietà al file di progetto o a un file Directory.Build.props. Per un elenco di proprietà utili per i progetti .NET, vedere Informazioni di riferimento su MSBuild per i progetti .NET SDK.

Suggerimento

Un modo semplice per creare un nuovo file Directory.Build.props dalla riga di comando consiste nell'usare il comando dotnet new buildprops nella radice del repository.

Destinazioni personalizzate

I progetti .NET possono creare pacchetti di destinazioni e proprietà di MSBuild personalizzate per l'uso da parte di progetti che utilizzano il pacchetto. Usare questo tipo di estendibilità quando si vuole:

  • Estendere il processo di compilazione.
  • Accedere agli artefatti del processo di compilazione, ad esempio i file generati.
  • Esaminare la configurazione in cui viene richiamata la compilazione.

È possibile aggiungere destinazioni o proprietà di compilazione personalizzate inserendo i file nel formato <package_id>.targets o <package_id>.props (ad esempio, Contoso.Utility.UsefulStuff.targets) nella cartella build del progetto.

Il codice XML seguente è un frammento di codice di un file con estensione csproj che indica al comando dotnet pack cosa inserire nel pacchetto. L'elemento <ItemGroup Label="dotnet pack instructions"> inserisce i file di destinazione nella cartella build all'interno del pacchetto. L'elemento <Target Name="CollectRuntimeOutputs" BeforeTargets="_GetPackageFiles"> inserisce gli assembly e i file json nella cartella build.

<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>

Per utilizzare una destinazione personalizzata nel progetto, aggiungere un elemento PackageReference che punta al pacchetto e alla relativa versione. A differenza degli strumenti, il pacchetto di destinazioni personalizzate è incluso nella chiusura delle dipendenze del progetto che le utilizza.

È possibile configurare come usare la destinazione personalizzata. Dal momento che si tratta di una destinazione MSBuild, può dipendere da una destinazione specifica, essere eseguita dopo un'altra destinazione e anche essere chiamata manualmente mediante il comando dotnet msbuild -t:<target-name>. Per offrire un'esperienza utente migliore, è tuttavia possibile combinare strumenti per progetto e destinazioni personalizzate. In questo scenario lo strumento per progetto accetta tutti i parametri necessari e li converte nella chiamata di dotnet msbuild richiesta che esegue la destinazione. È possibile visualizzare un esempio di questo tipo di sinergia nel repository degli esempi di MVP Summit 2016 Hackathon nel progetto dotnet-packer.

Vedi anche