.NET SDKs für Projekte

Projekten für .NET Core und .NET 5 und höher ist ein SDK (Software Development Kit) zugeordnet. Jedes Projekt-SDK besteht aus einer Reihe von MSBuild-Zielen und zugehörigen Tasks, mit denen Code kompiliert, paketiert und veröffentlicht wird. Ein Projekt, das auf ein Projekt-SDK verweist, wird zuweilen auch als Projekt im SDK-Stil bezeichnet.

Verfügbare SDKs

Folgende SDKs sind verfügbar:

id Beschreibung Repository
Microsoft.NET.Sdk .NET SDK https://github.com/dotnet/sdk
Microsoft.NET.Sdk.Web Das .NET Web SDK https://github.com/dotnet/sdk
Microsoft.NET.Sdk.BlazorWebAssembly Das .NET Blazor WebAssembly SDK https://github.com/dotnet/aspnetcore
Microsoft.NET.Sdk.Razor Das .NET Razor SDK https://github.com/dotnet/aspnetcore
Microsoft.NET.Sdk.Worker .NET SDK für Workerdienste
Microsoft.NET.Sdk.WindowsDesktop .NET SDK für die Desktopentwicklung, das Windows Forms (WinForms) und Windows Presentation Foundation (WPF) enthält* https://github.com/dotnet/winforms und https://github.com/dotnet/wpf

Das .NET SDK ist das Basis-SDK für .NET. Die anderen SDKs verweisen auf das .NET SDK, und Projekten, die den anderen SDKs zugeordnet sind, stehen alle .NET SDK-Eigenschaften zur Verfügung. Das Web SDK ist z. B. sowohl vom .NET SDK als auch vom Razor SDK abhängig.

Sie können auch selbst ein SDK erstellen, das über NuGet verteilt werden kann.

* Ab .NET 5 sollten Windows Forms- und Windows Presentation Foundation-Projekte (WPF) das .NET SDK (Microsoft.NET.Sdk) anstelle von Microsoft.NET.Sdk.WindowsDesktop angeben. Wenn Sie für diese Projekte TargetFramework auf net5.0-windows und UseWPF oder UseWindowsForms auf true festlegen, wird das Windows Desktop SDK automatisch importiert. Wenn Ihr Projekt auf .NET 5 oder höher ausgerichtet ist und das Microsoft.NET.Sdk.WindowsDesktop SDK angibt, wird die Buildwarnung NETSDK1137 ausgelöst.

Projektdateien

.NET-Projekte basieren auf dem MSBuild-Format. Projektdateien mit der Erweiterung .csproj für C#-Projekte und .fsproj für F#-Projekte, weisen das XML-Format auf. Das Stammelement einer MSBuild-Projektdatei ist das Element Project. Das Project-Element besitzt ein optionales Sdk-Attribut, das angibt, welches SDK (und welche Version) verwendet werden soll. Damit Sie die .NET-Tools verwenden und Ihren Code erstellen können, müssen Sie das Attribut Sdk auf eine der IDs in der Tabelle Available SDKs (Verfügbare SDKs) festlegen.

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

Um ein aus NuGet stammendes SDK anzugeben, schließen Sie die Version am Ende des Namens ein, oder geben Sie den Namen und die Version in der Datei global.json an.

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

Das Sdk-Element auf der obersten Ebene ist eine weitere Möglichkeit, das SDK anzugeben:

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

Wenn Sie mit einer dieser Möglichkeiten auf ein SDK verweisen, werden Projektdateien für .NET erheblich vereinfacht. Während der Auswertung des Projekts fügt MSBuild implizite Importe in der Projektdatei hinzu: im oberen Bereich für Sdk.props, im unteren Bereich für Sdk.targets.

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

Tipp

Auf einem Windows-Computer befinden sich die Dateien Sdk.props und Sdk.targets im Ordner %ProgramFiles%\dotnet\sdk\[version]\Sdks\Microsoft.NET.Sdk\Sdk.

Vorverarbeiten der Projektdatei

Mithilfe des Befehls dotnet msbuild -preprocess können Sie ein vollständig erweitertes Projekt so anzeigen, wie es für MSBuild dargestellt wird, nachdem das SDK und die zugehörigen Ziele angegeben wurden. Die Option preprocess des Befehls dotnet msbuild zeigt die importierten Dateien, ihre Quellen und ihren Beitrag zum Build, ohne das Projekt tatsächlich zu erstellen.

Wenn das Projekt mehrere Zielframeworks umfasst, geben Sie die Ergebnisse des Befehls nur für ein Framework aus, indem Sie dieses als MSBuild-Eigenschaft angeben. Zum Beispiel:

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

Standardmäßige Aufnahmen und Ausschlüsse

Die standardmäßigen Aufnahmen und Ausschlüsse für Compile-Elemente, eingebettete Ressourcen und None-Elemente sind im SDK definiert. Im Gegensatz zu SDK-Projekten, die nicht das .NET Framework als Ziel verwenden, müssen Sie diese Elemente nicht in Ihrer Projektdatei angeben, da die Standardwerte die meisten gängigen Anwendungsfälle abdecken. Durch dieses Verhalten wird die Projektdatei kleiner, verständlicher und kann bei Bedarf manuell bearbeitet werden.

Die folgende Tabelle zeigt, welche Elemente und welche Globs im .NET SDK enthalten bzw. nicht enthalten sind:

Element Glob einschließen Glob ausschließen Glob entfernen
Compile **/*.cs (oder andere Spracherweiterungen) **/*.user; **/*.*proj; **/*.sln; **/*.vssscc
EmbeddedResource **/*.resx **/*.user; **/*.*proj; **/*.sln; **/*.vssscc
None **/* **/*.user; **/*.*proj; **/*.sln; **/*.vssscc **/*.cs; **/*.resx

Hinweis

Die Ordner ./bin und ./obj aus, die von den MSBuild-Eigenschaften $(BaseOutputPath) und $(BaseIntermediateOutputPath) dargestellt werden, sind standardmäßig von den Globs ausgeschlossen. Ausschlüsse werden durch die DefaultItemExcludes-Eigenschaft dargestellt.

Das .NET SDK für die Desktopentwicklung verfügt über mehr einzuschließende und auszuschließende Elemente für WPF. Weitere Informationen finden Sie im Abschnitt Standard-Includes und -Excludes für WPF.

Buildfehler

Wenn Sie beliebige dieser Elemente explizit in Ihrer Projektdatei definieren, erhalten Sie wahrscheinlich eine „NETSDK1022“-Buildfehlermeldung ähnlich der folgenden:

Doppelte Compile-Elemente wurden eingeschlossen. Das .NET SDK enthält Compile-Elemente aus Ihrem Projektverzeichnis in der Standardeinstellung. Sie können diese Elemente aus Ihrer Projektdatei entfernen oder die Eigenschaft „EnableDefaultCompileItems“ auf FALSE festlegen, wenn Sie sie explizit in Ihrer Projektdatei einschließen möchten.

Doppelte EmbeddedResource-Elemente wurden eingeschlossen. Das .NET SDK enthält EmbeddedResource-Elemente aus Ihrem Projektverzeichnis in der Standardeinstellung. Sie können diese Elemente aus Ihrer Projektdatei entfernen oder die Eigenschaft „EnableDefaultEmbeddedResourceItems“ auf FALSE festlegen, wenn Sie sie explizit in Ihrer Projektdatei einschließen möchten.

Zum Beheben der Fehler führen Sie eine der folgenden Aktionen aus:

  • Entfernen Sie die expliziten Compile-, EmbeddedResource- oder None-Elemente, die den in der vorherigen Tabelle aufgeführten impliziten Elementen entsprechen.

  • Um jedes implizite Einbeziehen von Dateien zu deaktivieren, legen Sie die EnableDefaultItems-Eigenschaft auf false fest:

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

    Wenn Sie Dateien angeben möchten, die mit Ihrer Anwendung veröffentlicht werden sollen, können Sie weiterhin die bekannten MSBuild-Mechanismen dafür verwenden, wie etwa das Content-Element.

  • Deaktivieren Sie selektiv nur Compile-, EmbeddedResource- oder None-Globs, indem Sie die EnableDefaultCompileItems-, EnableDefaultEmbeddedResourceItems- oder EnableDefaultNoneItems-Eigenschaft auf false festlegen:

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

    Wenn Sie nur Compile-Globs deaktivieren, zeigt der Projektmappen-Explorer in Visual Studio die *.cs-Elemente eingeschlossen als None-Elemente weiterhin als Teil des Projekts an. Um das implizite None-Glob zu deaktivieren, legen Sie auch EnableDefaultNoneItems auf false fest.

Implizite using-Anweisungen

Ab .NET 6 werden neuen C#-Projekten implizite global using-Anweisungen hinzugefügt. Das bedeutet, dass Sie in diesen Namespaces definierte Typen verwenden können, ohne ihren vollqualifizierten Namen angeben oder manuell eine using-Anweisung hinzufügen zu müssen. Der implizite Aspekt bezieht sich auf die Tatsache, dass die global using-Anweisungen einer generierten Datei im obj-Verzeichnis des Projekts hinzugefügt werden.

Implizite global using-Anweisungen werden für Projekte hinzugefügt, die eines der folgenden SDKs verwenden:

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

Eine global using-Anweisung wird für jeden Namespace in einer Gruppe von Standardnamespaces hinzugefügt, die auf dem SDK des Projekts basieren. Diese Standardnamespaces sind in der folgenden Tabelle dargestellt.

SDK Standardnamespaces
Microsoft.NET.Sdk System
System.Collections.Generic
System.IO
System.Linq
System.Net.Http
System.Threading
System.Threading.Tasks
Microsoft.NET.Sdk.Web Microsoft.NET.Sdk-Namespaces
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.NET.Sdk-Namespaces
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
System.IO entfernt
System.Net.Http entfernt

Wenn Sie dieses Feature deaktivieren oder implizite global using-Anweisungen in einem vorhandenen C#-Projekt aktivieren möchten, können Sie dies über die MSBuild Eigenschaft ImplicitUsings erreichen.

Sie können auch zusätzliche implizite global using-Anweisungen hinzufügen, indem Sie Ihrer Projektdatei Using-Elemente hinzufügen (oder Import-Elemente für Visual Basic), beispielsweise:

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

Implizite Paketverweise

Für .NET Core 1.0-2.2 oder .NET Standard 1.0-2.0 fügt das .NET SDK implizite Verweise auf bestimmte Metapakete hinzu. Ein Metapaket ist ein frameworkbasiertes Paket, das nur aus Abhängigkeiten von anderen Paketen besteht. Es wird auf der Grundlage des/der Zielfameworks, das/die in der TargetFramework- oder TargetFrameworks-Eigenschaft Ihrer Projektdatei angegeben wurde/n, implizit auf Metapakete verwiesen.

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

Bei Bedarf können Sie implizite Paketverweise mithilfe der DisableImplicitFrameworkReferences-Eigenschaft deaktivieren und explizite Verweise nur auf die benötigten Frameworks oder Pakete hinzufügen.

Empfehlungen:

  • Fügen Sie für .NET Framework, .NET Core 1.0-2.2 oder .NET Standard 1.0-2.0 keine expliziten Verweise auf die Microsoft.NETCore.App- oder NETStandard.Library-Metapakete über das <PackageReference>-Element in Ihrer Projektdatei hinzu. Für .NET Core 1.0-2.2- und .NET Standard 1.0-2.0-Projekte wird implizit auf diese Metapakete verwiesen. Wenn für .NET Framework-Projekte bei der Verwendung eines auf .NET Standard basierenden NuGet-Pakets eine bestimmte Version von NETStandard.Library benötigt wird, installiert NuGet diese automatisch.
  • Wenn Sie für .NET Core 1.0-2.2 eine bestimmte Version der Runtime benötigen, sollten Sie die <RuntimeFrameworkVersion>-Eigenschaft in Ihrem Projekt (z. B. 1.0.4) verwenden, anstatt auf Metapakete zu verweisen. Sie benötigen z. B. möglicherweise eine bestimmte Patchversion der 1.0.0 LTS-Runtime, wenn Sie eigenständige Bereitstellungen verwenden.
  • Wenn Sie für .NET Standard 1.0-2.0 eine bestimmte Version des NETStandard.Library-Metapakets benötigen, können Sie die <NetStandardImplicitPackageVersion>-Eigenschaft verwenden und die Version festlegen, die Sie benötigen.

Buildereignisse

Verwenden Sie in Projekten im SDK-Format ein MSBuild-Ziel namens PreBuild oder PostBuild, und legen Sie die Eigenschaft BeforeTargets für PreBuild oder die Eigenschaft AfterTargets für PostBuild fest.

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

Hinweis

  • Sie können die MSBuild-Ziele beliebig benennen. Die Visual Studio-IDE erkennt jedoch PreBuild- und PostBuild-Ziele. Wenn Sie diese Namen verwenden, können Sie also Befehle in der IDE bearbeiten.
  • Die Eigenschaften PreBuildEvent und PostBuildEvent sollten in SDK-Projekten nicht verwendet werden, da Makros wie $(ProjectDir) nicht aufgelöst werden. Beispielsweise wird der folgende Code nicht unterstützt:
<PropertyGroup>
  <PreBuildEvent>"$(ProjectDir)PreBuildEvent.bat" "$(ProjectDir)..\" "$(ProjectDir)" "$(TargetDir)"</PreBuildEvent>
</PropertyGroup>

Anpassen des Builds

Es gibt verschiedene Möglichkeiten, einen Build anzupassen. Sie können eine Eigenschaft überschreiben, indem Sie sie als Argument an einen msbuild- oder dotnet-Befehl übergeben. Sie können die Eigenschaft auch zur Projektdatei oder zu einer Directory.Build.props-Datei hinzufügen. Eine Liste mit nützlichen Eigenschaften für .NET-Projekte finden Sie in der MSBuild-Referenz für .NET Core SDK-Projekte.

Tipp

Eine einfache Möglichkeit, eine neue Datei Directory.Build.props über die Befehlszeile zu erstellen, besteht darin, den Befehl dotnet new buildprops im Stammverzeichnis Ihres Repositorys zu verwenden.

Benutzerdefinierte Ziele

.NET-Projekte können benutzerdefinierte MSBuild-Ziele und -Eigenschaften für Projekte in Pakete packen, die das Paket nutzen. Verwenden Sie diese Art der Erweiterbarkeit, wenn Sie Folgendes vorhaben:

  • Erweitern des Buildprozesses
  • Zugreifen auf Artefakte des Buildprozesses, z. B. generierte Dateien
  • Untersuchen einer Konfiguration, in der der Build aufgerufen wird

Sie fügen benutzerdefinierte Buildziele oder -eigenschaften hinzu, indem Sie Dateien im Format <package_id>.targets oder <package_id>.props (z. B.Contoso.Utility.UsefulStuff.targets) im build-Ordner des Projekts platzieren.

Der XML-Code ist ein Codeausschnitt aus einer .csproj-Datei, die den Befehl dotnet pack anweist, was paketiert werden soll. Das Element <ItemGroup Label="dotnet pack instructions"> platziert die Zieledateien innerhalb des Pakets in den Ordner build. Das Element <Target Name="CollectRuntimeOutputs" BeforeTargets="_GetPackageFiles"> platziert die Assemblys und .json-Dateien in den Ordner 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>

Damit ein benutzerdefiniertes Ziel in Ihrem Projekt genutzt werden kann, fügen Sie ein PackageReference-Element hinzu, das auf das Paket und dessen Version zeigt. Im Gegensatz zu den Tools wird das benutzerdefinierte Zielpaket in die Abhängigkeiten des nutzenden Projekts eingeschlossen.

Sie können konfigurieren, wie das benutzerdefinierte Ziel genutzt werden soll. Da es sich um ein MSBuild-Ziel handelt, kann es von einem angegebenen Ziel abhängig sein, nach einem anderen Ziel ausgeführt werden oder mit dem Befehl dotnet msbuild -t:<target-name> manuell aufgerufen werden. Wenn Sie jedoch ein besseres Benutzererlebnis bieten möchten, können Sie projektbezogene Tools und benutzerdefinierte Ziele kombinieren. In diesem Szenario akzeptiert das projektbezogene Tool alle erforderlichen Parameter und übersetzt diese in den erforderlichen dotnet msbuild-Aufruf, der das Ziel ausführt. Sie sehen ein Beispiel dieser Art von Synergie im Repository mit den MVP Summit 2016 Hackathon-Beispielen im Projekt dotnet-packer.

Siehe auch