September 2017

Band 32, Nummer 9

.NET Standard: Entmystifizierung von .NET Core und .NET Standard

Von Immo Landwerth | September 2017

Die neuesten Mitglieder der .NET-Produktfamilie sorgen für einige Verwirrung bezüglich .NET Core und .NET Standard und deren Unterschiede zu .NET Framework. In diesem Artikel untersuche ich jedes dieser Produkte und erläutere, unter welchen Umständen es eingesetzt werden sollte.

Bevor wir uns mit den Einzelheiten befassen, empfiehlt es sich, .NET in einem größeren Rahmen zu betrachten, damit .NET Core und .NET Standard in diesen eingeordnet werden können. Als .NET Framework vor 15 Jahren erstmals veröffentlicht wurde, handelte es sich um einen einfachen .NET-Stapel, der zum Erstellen von Windows-Desktop- und -Webanwendungen verwendet werden konnte. Seit damals wurden weitere .NET -Implementierungen eingeführt (z. B. Xamarin), die Sie zum Erstellen mobiler Apps für iOS und Android sowie von macOS-Desktopanwendungen verwenden können. Abbildung 1 zeigt dies.

Die .NET-Produktfamilie

Abbildung 1: Die .NET-Produktfamilie

.NET Core und .NET Standard lassen sich wie folgt einordnen:

  • .NET Core: Dies ist die neueste .NET-Implementierung. Diese ist Open Source und für mehrere Betriebssysteme verfügbar. Mit .NET Core können Sie plattformübergreifende Konsolen-Apps und ASP.NET Core-Webanwendungen sowie -Clouddienste erstellen.
  • .NET Standard: Dies ist die Sammlung der grundlegenden APIs (häufig als Basisklassenbibliothek oder BCL bezeichnet), die alle .NET-Implementierungen implementieren müssen. Mithilfe von .NET Standard können Sie Bibliotheken erstellen, die für alle .NET-Apps unabhängig davon gemeinsam verwendet werden können, welche .NET-Implementierung oder welches Betriebssystem für die Ausführung eingesetzt wird.

Einführung in .NET Core

.NET Core ist eine neue plattformübergreifende Open Source .NET-Implementierung, die aus .NET Framework und Silverlight geforkt wurde. Sie ist für mobile und Serverworkloads optimiert, indem eigenständige XCOPY-Bereitstellungen ermöglicht werden.

Damit Sie einen besseren Eindruck von .NET Core bekommen, sollten wir etwas näher untersuchen, wie die Entwicklung für .NET Core aussieht. Dabei können wir uns außerdem mit den neuen befehlszeilenbasierten Tools vertraut machen. Sie können auch Visual Studio 2017 für die .NET Core-Entwicklung verwenden. Da Sie diesen Artikel lesen, ist es aber sehr wahrscheinlich, dass Sie bereits mit Visual Studio vertraut sind. Deshalb möchte ich den Schwerpunkt auf die neuen Funktionen legen.

Als .NET erstellt wurde, war das Framework hochgradig für schnelle Anwendungsentwicklung unter Windows optimiert. In der Praxis bedeutete dies, dass die .NET-Entwicklung und Visual Studio untrennbar miteinander verbunden waren. Und eines ist sicher: Das Verwenden von Visual Studio für die Entwicklung macht einfach Spaß. Die Produktivität ist extrem hoch, und der integrierte Debugger ist der beste, den ich jemals verwendet habe.

Unter bestimmten Umständen ist die Verwendung von Visual Studio jedoch nicht die bequemste Option. Angenommen, Sie möchten einfach mit .NET herumspielen, um C# zu lernen: Sie sollte zu diesem Zweck nicht eine mehrere GB große IDE herunterladen und installieren müssen. Auch beim Zugriff auf einen Linux-Computer über SSH ist die Verwendung einer IDE einfach keine Option. Vielleicht sind Sie auch ein Entwickler, der die Verwendung einer Befehlszeilenschnittstelle (Command-Line Interface, CLI) schlichtweg vorzieht.

Aus diesem Grund wurde eine erstklassige CLI namens .NET Core CLI erstellt. Der primäre Treiber der .NET Core CLI nennt sich „dotnet“. Sie können ihn für fast alle Aspekte Ihrer Entwicklung verwenden, z. B. für das Erstellen, den Buildvorgang, das Testen und die Paketerstellung von Projekten. Sehen wir uns an, wie dies funktioniert.

Beginnen Sie, indem Sie eine Konsolen-App „Hello World“ erstellen und ausführen (ich verwende PowerShell unter Windows, aber dies funktioniert ebenso gut mit Bash unter macOS oder Linux):

$ dotnet new console -o hello
$ cd hello
$ dotnet run
Hello World!

Der Befehl „dotnet new“ ist das CLI-Äquivalent von „Datei“ > „Neues Projekt“ in Visual Studio. Sie können eine Vielzahl verschiedener Projekttypen erstellen. Geben Sie „dotnet new“ ein, um die verschiedenen vorinstallierten Vorlagen anzuzeigen.

Extrahieren wir nun etwas Programmlogik in eine Klassenbibliothek. Erstellen Sie zu diesem Zweck zunächst ein Klassenbibliothekprojekt, das parallel zu Ihrem Hello World-Projekt ist:

$ cd ..
$ dotnet new library -o logic
$ cd logic

Die Programmlogik, die Sie kapseln möchten, ist die Konstruktion einer Hello World-Meldung. Ändern Sie also den Inhalt von „Class1.cs“ in den folgenden Code:

namespace logic
{
  public static class HelloWorld
  {
      public static string GetMessage(string name) => $"Hello {name}!";
  }
}

An diesem Punkt sollten Sie auch „Class1.cs“ in „HelloWorld.cs“ umbenennen:

$ mv Class1.cs HelloWorld.cs

Beachten Sie, dass Sie die Projektdatei für diese Änderung nicht aktualisieren müssen. Die in .NET Core verwendeten neuen Projektdateien schließen einfach alle Quelldateien aus dem Verzeichnis des Projekts ein. Aus diesem Grund ist beim Hinzufügen, Entfernen oder Umbenennen von Dateien das Ändern des Projekts nicht mehr erforderlich. Dateivorgänge sind so über die Befehlszeile einfacher auszuführen.

Damit die HelloWorld-Klasse verwendet werden kann, müssen Sie die Hello-App so aktualisieren, dass sie auf die Logikbibliothek verweist. Bearbeiten Sie dazu die Projektdatei, oder verwenden Sie den Befehl „dotnet add reference“:

$ cd ../hello
$ dotnet add reference ../logic/logic.csproj

Aktualisieren Sie nun die Datei „Program.cs“ für die Verwendung der HelloWorld-Klasse. Abbildung 2 zeigt dies.

Abbildung 2: Aktualisieren der Datei „Program.cs“ für die Verwendung der HelloWorld-Klasse

using System;
using logic;
namespace hello
{
class Program
{
static void Main(string[] args)
{
Console.Write("What's your name: ");
var name = Console.ReadLine();
var message = HelloWorld.GetMessage(name);
Console.WriteLine(message);
}
}
}

Geben Sie zum Erstellen und Ausführen Ihrer App einfach „dotnet run“ ein:

$ dotnet run
What's your name: Immo
Hello Immo!

Sie können auch Tests über die Befehlszeile erstellen. Die CLI unterstützt MSTest sowie das beliebte xUnit-Framework. In diesem Beispiel verwenden wir xUnit:

$ cd ..
$ dotnet new xunit -o tests
$ cd tests
$ dotnet add reference ../logic/logic.csproj

Ändern Sie den Inhalt von „UnitTest1.cs“, um einen Test hinzuzufügen. Abbildung 3 zeigt dies.

Abbildung 3: Ändern des Inhalts von „UnitTest1.cs“, um einen Test hinzuzufügen

using System;
using Xunit;
using logic;
namespace tests
{
public class UnitTest1
{
[Fact]
public void Test1()
{
var expectedMessage = "Hello Immo!";
var actualMessage = HelloWorld.GetMessage("Immo");
Assert.Equal(expectedMessage, actualMessage);
}
}
}

Nun können Sie die Tests ausführen, indem Sie „dotnet test“ aufrufen:

$ dotnet test
Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.

Erstellen wir nun eine einfache ASP.NET Core-Website, um die Sache etwas interessanter zu machen:

$ cd ..
$ dotnet new web -o web
$ cd web
$ dotnet add reference ../logic/logic.csproj

Bearbeiten Sie die Datei „Startup.cs“, und ändern Sie den Aufruf von „app.Run“ so, dass die Klasse „HelloWorld“ wie folgt verwendet wird:

app.Run(async (context) =>
{
  var name = Environment.UserName;
  var message = logic.HelloWorld.GetMessage(name);
  await context.Response.WriteAsync(message);
});

Zum Starten des Entwicklungswebservers verwenden Sie einfach „dotnet run“ erneut:

$ dotnet run
Hosting environment: Production
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

Navigieren Sie zur angezeigten URL, die http://localhost:5000 lauten sollte.

An diesem Punkt sollte Ihre Projektstruktur wie Abbildung 4 aussehen.

Abbildung 4: Die von Ihnen erstellte Projektstruktur

$ tree /f
│
├───hello
│ hello.csproj
│ Program.cs
│
├───logic
│ HelloWorld.cs
│ logic.csproj
│
├───tests
│ tests.csproj
│ UnitTest1.cs
│
└───web
Program.cs
Startup.cs
web.csproj

Um die Bearbeitung der Dateien mit Visual Studio zu vereinfachen, erstellen wir auch eine Projektmappendatei und fügen der Projektmappe alle Projekte hinzu:

$ cd ..
$ dotnet new sln -n HelloWorld
$ ls -fi *.csproj -rec | % { dotnet sln add $_.FullName }

Wie Sie sehen können, ist die CLI von .NET Core leistungsfähig und bringt eine schlanke Funktionalität mit sich, die auch Entwicklern mit einem anderen Hintergrund recht vertraut vorkommen sollte. Auch wenn wir in diesem Beispiel „dotnet“ mit PowerShell unter Windows verwendet haben, sieht die Funktionalität unter Linux oder macOS recht ähnlich aus.

Ein weiterer großer Vorteil von .NET Core besteht darin, dass eigenständige Bereitstellungen unterstützt werden. Sie könnten Ihre Anwendung unter Verwendung von Docker so in Containern kapseln, dass sie über ihre eigene Kopie der .NET Core Runtime verfügt. Auf diese Weise können Sie verschiedene Anwendungen auf dem gleichen Computer ausführen, die unterschiedliche .NET Core-Versionen verwenden, ohne dass dies zu Konflikten führt. Da .NET Core Open Source ist, können Sie auch Nightly Builds oder sogar Versionen einschließen, die Sie geändert oder selbst erstellt haben, und so von Ihnen vorgenommene Änderungen bereitstellen. Dies zu erläutern würde jedoch den Rahmen dieses Artikels sprengen.

Einführung in .NET Standard

Wenn Sie moderne Benutzeroberflächen erstellen, umfasst Ihre App häufig mehrere Formfaktoren und daher auch mehrere .NET-Implementierungen. Heutzutage erwarten Kunden einfach, dass sie Ihre Web-App auf ihrem Smartphone nutzbar ist und Daten über ein cloudbasiertes Back-End freigegeben werden können. Wenn ein Laptop verwendet wird, möchten Benutzer auch über eine Website Zugriff erlangen. In Ihrer eigenen Infrastruktur möchten Sie wahrscheinlich Befehlszeilentools und möglicherweise sogar Desktop-Apps verwenden, damit Ihre Mitarbeiter das System verwalten können. Abbildung 5 zeigt das Zusammenspiel der verschiedenen .NET-Implementierungen für dieses Szenario.

Abbildung 5: Beschreibungen von .NET-Implementierungen

  Betriebssystem Bin Open-Source- Zweck
.NET Framework Windows Nein Wird zum Erstellen von Windows-Desktopanwendungen und ASP.NET-Web-Apps verwendet, die unter IIS ausgeführt werden.
.NET Core Windows, Linux, macOS Ja Wird zum Erstellen von plattformübergreifenden Konsolen-Apps und ASP.NET Core-Web-Apps sowie -Clouddiensten verwendet.
Xamarin iOS, Android, macOS Ja Wird zum Erstellen mobiler Anwendungen für iOS und Android sowie von Desktop-Apps für macOS verwendet.
.NET Standard N/A Ja Wird zum Erstellen von Bibliotheken verwendet, auf die aus allen .NET-Implementierungen (z. B. .NET Framework, .NET Core und Xamarin) verwiesen werden kann.

In einer solchen Umgebung wird das Freigeben von Code zu einer größeren Herausforderung. Sie müssen verstehen, wo APIs verfügbar sind, und sicherstellen, dass freigegebene Komponenten nur APIs verwenden, die in allen .NET-Implementierungen verfügbar sind, die Sie verwenden.

Hier greift .NET Standard. .NET Standard ist eine Spezifikation. Jede .NET Standard-Version definiert eine Sammlung von APIs, die alle .NET-Implementierungen bereitstellen müssen, damit sie mit dieser Version kompatibel sind. Sie können dies als einen weiteren .NET-Stapel ansehen. Nur können für ihn keine Apps erstellt werden, sondern ausschließlich Bibliotheken. Es handelt sich um die .NET-Implementierung, die Sie für Bibliotheken verwenden sollten, auf die Sie von überall her verweisen möchten.

Sie fragen sich vielleicht, welche APIs .NET Standard umfasst. Wenn Sie mit .NET Framework vertraut sind, sollte Ihnen die BCL bekannt sein, die ich weiter oben bereits erwähnt habe. Die BCL ist die Sammlung grundlegender APIs, die von UI-Frameworks und Anwendungsmodellen unabhängig sind. Sie umfasst die primitiven Typen, Datei-E/A, Netzwerkdienste, Reflexion, Serialisierung, XML und vieles mehr.

Alle .NET-Stapel implementieren eine Version von .NET Standard. Als Faustregel gilt dabei: Wenn eine neue Version einer .NET-Implementierung erstellt wird, implementiert diese normalerweise die neueste verfügbare Version von .NET Standard.

HTML und Browser stellen eine gute Analogie dar: Dabei entspricht die HTML-Spezifikation .NET Standard, und die verschiedenen Browser gleichen den .NET-Implementierungen, z. B. .NET Framework, .NET Core und Xamarin.

An diesem Punkt sind Sie vielleicht neugierig geworden, wie Sie .NET Standard verwenden können. Tatsächlich haben Sie .NET Standard bereits eingesetzt. Erinnern Sie sich daran, dass wir weiter oben die Klassenbibliothek für Programmlogik erstellt haben? Sehen wir uns die Projektdatei etwas genauer an.

$ cd logic
$ cat logic.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

</Project>

Stellen wir dem die Projektdatei der Hello-Konsolenanwendung gegenüber:

$ cd ..\hello
$ cat hello.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <ProjectReference Include="..\logic\logic.csproj" />
  </ItemGroup>

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

</Project>

Wie Sie erkennen können, weist die Logikbibliothek für „TargetFramework“ den Wert „netstandard2.0“ auf, während die Konsolen-App den Wert „netcoreapp2.0“ verwendet. Die TargetFramework-Eigenschaft gibt an, welche .NET-Implementierung als Ziel verwendet wird. Die Konsolen-App verwendet in unserem Beispiel .NET Core 2.0 als Ziel, die Bibliothek .NET Standard 2.0. Dies bedeutet, dass Sie auf die Logikbibliothek nicht nur aus einer .NET Core-App, sondern auch aus einer App verweisen können, die für .NET Framework oder Xamarin erstellt wurde.

Leider verwenden die meisten der zurzeit verfügbaren Bibliotheken noch nicht .NET Standard als Ziel. Die meisten Bibliotheken verwenden .NET Framework als Ziel. Natürlich können (oder sollten) nicht alle Bibliotheken .NET Standard als Ziel verwenden. Eine Bibliothek, die WPF-Steuerelemente (Windows Presentation Foundation) enthält, muss z. B. .NET Framework als Ziel verwenden, weil Benutzeroberflächen nicht Bestandteil des Standards sind. Viele der Universalbibliotheken verwenden jedoch nur .NET Framework als Ziel, weil sie erstellt wurden, als .NET Standard einfach noch nicht existierte.

Mit .NET Standard 2.0 ist die API-Sammlung groß genug, damit die meisten (wenn nicht alle) Universalbibliotheken .NET Standard als Ziel verwenden können. Als Ergebnis verwenden 70 Prozent aller in NuGet verfügbaren Bibliotheken zurzeit ausschließlich APIs, die nun Bestandteil von .NET Standard sind. Trotzdem ist nur ein Bruchteil von ihnen ausdrücklich als kompatibel mit .NET Standard gekennzeichnet.

Damit Entwickler diese Bibliotheken verwenden können, wurde ein Kompatibilitätsmodus hinzugefügt. Wenn Sie ein NuGet-Paket installieren, das keine Bibliothek für Ihr Zielframework und auch keine Bibliothek für .NET Standard anbietet, versucht NuGet, ein Fallback auf .NET Framework auszuführen. Dies bedeutet, dass Sie auf .NET Framework-Bibliotheken so verweisen können, als wäre .NET Standard das Ziel.

Ich zeige Ihnen, wie das aussieht. In meinem Beispiel verwende ich eine gängige Sammlungsbibliothek namens PowerCollections, die im Jahr 2007 geschrieben wurde. Sie wurde eine ganze Zeit lang nicht aktualisiert und verwendet noch immer .NET Framework 2.0 als Ziel. Ich installiere diese Bibliothek aus NuGet in der Hello-App:

$ dotnet add package Huitian.PowerCollections

Diese Bibliothek stellt zusätzliche Sammlungstypen zur Verfügung, die die BCL nicht bietet, z. B. einen Behälter, der keine Sortierungsgarantien übernimmt. Ändern wir die Hello-App nun so, dass diese Bibliothek verwendet wird. Abbildung 6 zeigt dies.

Abbildung 6: Beispielanwendung, die PowerCollections verwendet

using System;
using Wintellect.PowerCollections;
namespace hello
{
class Program
{
static void Main(string[] args)
{
var data = new Bag<int>() { 1, 2, 3 };
foreach (var element in data)
Console.WriteLine(element);
}
}
}

Wenn Sie das Programm ausführen, wird Folgendes angezeigt:

$ dotnet run
hello.csproj : warning NU1701: Package 'Huitian.PowerCollections 1.0.0' was restored using '.NETFramework,Version=v4.6.1' instead of the project target framework '.NETCoreApp,Version=v2.0'. This may cause compatibility problems.
1
3
2

Was ist da eben passiert? Die Hello-App verwendet .NET Core 2.0 als Ziel. Da .NET Core 2.0 .NET Standard 2.0 implementiert, ist auch der Kompatibilitätsmodus verfügbar, um auf .NET Framework-Bibliotheken zu verweisen. Nicht alle .NET Framework-Bibliotheken funktionieren jedoch in allen .NET-Implementierungen. Sie verwenden z. B. möglicherweise Windows Forms oder WPF-APIs. NuGet kann dies nicht wissen und gibt eine Warnmeldung aus, damit Sie sich dieser Situation bewusst sind und keine Zeit mit der Behandlung von Problemen vergeuden, die sich hieraus ergeben können.

Beachten Sie, dass Sie diese Warnung bei jedem Buildvorgang erhalten. Dies verhindert Probleme, wenn Sie die Warnung während der Paketinstallation einfach nicht gesehen oder auch vergessen haben.

Natürlich gibt es nichts Schlimmeres als Warnungen, für die keine Aktionen möglich sind, und über die bei jedem Buildvorgang hinweggesehen werden muss. Sie können daher nach dem Überprüfen Ihrer App die Warnung für dieses Paket deaktivieren. Da die App ordnungsgemäß ausgeführt wird (sie hat den Inhalt des von Ihnen erstellten Behälters richtig ausgegeben), können Sie die Warnung nun unterdrücken. Zu diesem Zweck bearbeiten Sie die Datei „hello.csproj“ und fügen dem Paketverweis das NoWarn-Attribut hinzu:

<PackageReference Include="Huitian.PowerCollections" Version="1.0.0" 
  NoWarn="NU1701" />

Wenn Sie die App erneut ausführen, sollte die Warnung nicht mehr angezeigt werden. Wenn Sie ein weiteres Paket installieren, das den Kompatibilitätsmodus verwendet, erhalten Sie die Warnung für das Paket, für das sie unterdrückt werden kann, ebenfalls.

Mithilfe der neuen Tools können Klassenbibliothekprojekte auch NuGet-Pakete als Teil des Buildvorgangs generieren. Auf diese Weise wird es viel einfacher, Ihre Bibliotheken weltweit (durch Pushen auf nuget.org) oder nur innerhalb Ihrer Organisation (durch Pushen in Ihren eigenen Paketfeed unter Visual Studio Team Services oder MyGet) freizugeben. Die neuen Projekte unterstützen außerdem mehrere Ziele, damit Sie ein einzelnes Projekt für mehrere .NET-Implementierungen erstellen können. Dies bedeutet, dass Sie bedingte Kompilierung („#if“) verwenden können, um die Bibliothek an bestimmte .NET-Implementierungen anzupassen. Außerdem können Sie .NET Standard-Wrapper für plattformspezifische APIs erstellen. Alle diese Möglichkeiten sprengen jedoch den Rahmen dieses Artikels.

Zusammenfassung

.NET Standard ist eine Spezifikation von APIs, die alle .NET-Implementierungen bereitstellen müssen. .NET Standard verleiht der .NET-Produktfamilie Konsistenz und ermöglicht das Erstellen von Bibliotheken, die Sie aus jeder beliebigen .NET-Implementierung verwenden können. Es ersetzt PCLs zum Erstellen freigegebener Komponenten.

.NET Core ist eine Implementierung von .NET Standard und zum Erstellen von Konsolenanwendungen, Web-Apps und Clouddiensten unter Verwendung von ASP.NET Core optimiert. Das zugehörige SDK enthält leistungsfähige Tools, die zusätzlich zur Visual Studio-Entwicklung einen vollständigen, auf der Befehlszeile basierenden Entwicklungsworkflow unterstützen. Weitere Informationen zu diesen Tools finden Sie unter aka.ms/netstandardfaq und aka.ms/netcore.


Immo Landwerth arbeitet als Program Manager bei Microsoft an .NET. Er beschäftigt sich schwerpunktmäßig mit .NET Standard, der BCL und dem API-Entwurf.


Diesen Artikel im MSDN Magazine-Forum diskutieren