2. .NET Core

Der .NET-Kern Core 2 ist eine kleine, optimierte Laufzeitumgebung. Sie ersetzt das bekannte .NET-Framework und wird eingesetzt, wenn die gesamte Funktionsbreite des Frameworks nicht benötigt wird und stattdessen die Vorteile bei der Performance und Plattformunabhängigkeit relevant sind. Entfallen sind vor allem die Teile, die plattformspezifisch sind, also beispielsweise der Zugriff auf Active Directory, MSMQ, WCF, WPF etc.

Der Core bietet eine modulare Laufzeitumgebung für Windows, Linux und MacOS. Er besteht aus mehreren Bibliotheken, .NET Core genannt, sowie einer Laufzeit, Core CLR genannt. Der Core ist quelloffen (Open Source) und ist auf GitHub verfügbar:

  • .NET Core Libraries:
    • https://github.com/dotnet/corefx
  • .NET Core Common Language Runtime (CoreCLR):
    • https://github.com/dotnet/coreclr

Ergänzt wird dies durch das Kommandozeilenwerkzeug dotnet. In früheren Versionen wurde dies DNX genannt.

Die Bereitstellung erfolgt allerdings bevorzugt über Nuget (nuget.org). Die CoreFX-Bibliotheken sind einzeln verfügbar, um den Speicherverbrauch der finalen Anwendung so klein wie möglich zu halten. Der Stammnamensraum ist System. Jede Bibliothek ordnet sich darunter an: System.<LibName>. Die Idee dahinter ist die Auflösung der Abhängigkeit von Installationsvoraussetzungen. Die Bibliotheken werden immer mit der Anwendung ausgeliefert und auf dem Zielsystem muss nichts vorhanden sein. Mit der Anwendung wird sogar die Laufzeit CoreCLR verteilt, sodass alles was die Anwendung braucht verteilt wird. Verschiedene Versionen laufen Seite an Seite und völlig unabhängig. Einen Global Assembly Cache gibt es nicht mehr.

Die Standardbibliotheken sind beispielsweise: Collections, Console, Diagnostics, IO, LINQ, JSON und XML.

2.1 ASP.NET Core

Die neueste Entwicklungsumgebung für Web-Anwendungen ist ASP.NET Core. ASP.NET Core kann in der Laufzeitumgebung .NET 4.6 oder neuer oder auch mit .NET Core ausgeführt werden. Das ist freilich durch die wiederholte Benutzung des Begriffs “Core” etwas verwirrend. Generell gilt hier aber tatsächlich eine weitgehende Unabhängigkeit von der Laufzeit. In diesem Buch wird eine Applikationsumgebung entwickelt, die exemplarisch viele Funktionen vereint. Dazu gehört auch die Plattformunabhängigkeit, sodass der Code final mit .NET Core getestet wurde. Damit wurde auch bewusst auf jede API verzichtet, die hier nicht zur Verfügung steht.

2.2 Motivation

Das.NET-Framework war nie das alleinige, große, umfassende Rahmenwerk, das alle Entwickler zufriedenstellt. Angefangen mit dem Compact Framework bis zu Silverlight und WinRT gab es viele Varianten, Derivate und Sonderfälle. Auf anderen Plattformen setzte sich das mit Mono oder Xamarin fort. Dies alles sollte mit CoreFX vereinheitlicht werden. CoreFX wird nicht für alle Anwendungsfälle .NET ersetzen, aber in vielen Fällen reicht es aus und dann steht eine neue, einheitlich Basis für einfachere Umgebungen zur Verfügung.

CoreFX wird, wenn es irgendwann vollständig implementiert ist, nicht zwingend kleiner als .NET sein. Das ist nicht das Ziel. Die Modularisierung erlaubt es aber, dass Entwickler sich Teile herauslösen und nur das benutzen, was wirklich gebraucht wird. Dadurch ist die finale Anwendung in der Tat kleiner.

Der Host der Laufzeitumgebung – quasi die Startanwendung – ist das .NET Execution Environment. Dieses kann sowohl CoreFX als auch .NET ausführen. DNX-Projekt können für beide Laufzeitumgebungen lauffähig gemacht werden. Deshalb hat die Projektvorlage für ASP.NET Core bei Bedarf beide Referenzen:

1 "frameworks": {
2     "net472": { },
3     "netstandard2.0": { }
4 },

Dabei steht net472 für .NET 4.7.2 und netstandard für die .NET Core-Umgebung. netstandard2.0 dient dem Erstellen von portierbaren Assemblies. Wählen Sie stattdessen netcoreapp2.0, um eine eigenständige Applikation zu erstellen. Dies ist der Fall, wenn Sie eine ASP.NET Core Web-Applikation bauen.

Abbildung: Projektreferenzen für .NET und CoreFX
Abbildung: Projektreferenzen für .NET und CoreFX

Sollte im Code eine Abhängigkeit auftreten, so können Sie mit einer Compiler-Direktive darauf reagieren:

1 #if NET472
2   // Code der nur mit .NET-Framework läuft
3 #endif

.NET Standard

.NET Standard ist eine API-Beschreibung – nur Text, kein Code – die die vielen Laufzeit-Implementierungen vereinheitlichen soll. Aktuell ist .NET Standard Version 2.0. Wenn eine Assembly erstellt wird und als Ziel netstandard2.0 benutzt wird, so lässt sich diese Assembly gegen alle kompatiblen Projekte linken. Das schließt .NET 4.x ebenso ein wie .NET Core 2. Die Entscheidung, das Deployment der Anwendung gegen das eine oder andere oder beide Laufzeiten auszuführen, ist dem Bibliotheksentwickler deshalb egal. Dies ist ein großer Vorteil für die Zukunft wiederverwendbaren Codes.

Wechseln des Frameworks

Dieser Abschnitt ist nur für Windows und Visual Studio zutreffend. Auf den anderen Plattformen gibt es kein .NET 4 und deshalb auch keine Alternativen zu .NET Core.

Zum Wechseln des Frameworks ändern Sie den Eintrag in der Projektdatei .csproj. Nach dem Speichern fängt Visual Studio sofort an, das Paket herunterzuladen und bereitzustellen. Der Vorgang kann einen Moment dauern. Beobachten Sie die Statusleiste oder das Ausgabefenster, um den Vorgang zu überwachen. Wird nur ein Ziel unterstützt, sieht das folgendermaßen aus:

1 <PropertyGroup>
2   <TargetFramework>net462</TargetFramework>
3 </PropertyGroup>

Bei zwei Zielen wäre eine solche Variante denkbar (beachten Sie den Plural für den Tag-Namen):

1 <PropertyGroup>
2   <TargetFrameworks>netstandard2.0;net47</TargetFrameworks>
3 </PropertyGroup>

Microsoft empfiehlt, immer beide Versionen des Frameworks zu referenzieren – .NET Core und .NET 4.x. Natürlich können Sie die beschränken, in dem die eine oder die andere Referenz entfernt wird. Sollten Sie mit Techniken aus ASP.NET 4 oder früheren Versionen arbeiten, oder Teile des Projekts darin entwickeln oder Teile migrieren, ist .NET 4.x zwingend erforderlich. .NET Core hat dafür keine Unterstützung.

.NET Core und NuGet

NuGet ist das Repository, über das weitere Funktionsbausteine für .NET Core bereitgestellt werden. Stellen Sie sich das als Ersatz für lokale Ordner mit Assemblies oder auch als Ersatz für den Global Assembly Cache vor. Es wird also keine explizite Trennung mehr erkennbar sein zwischen .NET-Bausteinen und Angeboten anderer Anbieter (third party). Solche fremden Angebote können direkt auf .NET Core-Assemblies referenzieren, was die Distribution stabiler Pakete vereinfacht. Vor allem aber werden alle Abhängigkeiten mitgeführt. Das heißt, dass auf dem Zielsystem keine Voraussetzungen wie beispielsweise eine bestimmte .NET-Version herrschen müssen. Was benötigt wird, kommt mit der Applikation mit. Darüberhinaus benötigt kaum eine Anwendung alles. Das resultierende Paket ist deshalb in aller Regel kleiner als die klassische Kombination aus Framework und eigenen Assemblies.

.NET Core ist eine modulare Untermenge des .NET Frameworks. Es steht als quelloffen (Open Source) zur Verfügung und adressiert mehrere Plattformen. Es kann mit klassischen Framework-Anwendungen koexistieren.

2.3 Die Ausführungsumgebung

Die Ausführungsumgebung (.NET Execution Environment) besteht aus dem Entwickler-Kit (SDK) und der Laufzeitumgebung. Sie ist für Windows, Mac und Linux verfügbar. Sie bietet einen Host-Prozess, unter dem die Anwendung abläuft, eine Steuerlogik für die Laufzeit und einen definierten Einsprungpunkt zum Start der Applikation. Primär ist diese Umgebung auf ASP.NET optimiert, kann aber auch andere Programmarten ausführen, wie beispielsweise Konsolenapplikationen.

Der Vorteil der Ausführungsumgebung ist die plattformunabhängigkeit. Auch wenn die Entwicklung auf Windows stattfindet, kann die Produktionsumgebung Linux sein. Plattformspezifische Besonderheiten werden weitgehend ausgeblendet und müssen nur selten berücksichtigt werden. Die Konfigurationsumgebung basiert auf JSON (JavaScript Object Notation) und hat keinen Bezug zu bestimmten Plattformfunktionen. Die Integration mit anderen Paket-Managern, speziell NPM (Node Package Manager) und Bower, ist besonders einfach, weil diese ebenso auf JSON setzen. Pakete lassen sich global auf einer Maschine bereitstellen, um die fortlaufende Arbeit am Projekt zu vereinfachen.

Projekte

Ein Core-Projekt ist primäre ein Ordner mit Codedateien. Der Name des Projekts ist der Name des Ordners. Visual Studio – unter Windows – hat eine Projektverwaltung. Falls diese benutzt wird (ab VS 2017 ist dies optional), wird die Projektdatei benötigt. Alle Dateien im Ordner sind automatisch Teil des Projekts.

Die Werkzeuge

Paketabhängigkeiten können nur aufgelöst werden, wenn das Basispaket installiert wurde. Um Pakete manuell nachinstallieren zu können, wird das Kommandozeilenwerkzeug. Die nötigen Pakete werden zuerst in der Datei project.json festgelegt. Dann werden sie mittels dotnet wieder hergestellt:

dotnet restore

Falls eigene Pakte erstellt werden – also Applikationsbausteine zur Nutzung durch andere Applikationen – müssen diese zuerst veröffentlicht werden. Dies erfolgt im ersten Schritt lokal mit folgendem Kommando:

dotnet publish

Die Struktur die daraus entsteht hat einen festen Aufbau:

1 output/
2       /packages
3       /appName
4       /commandName.cmd

Der Ordner output enthält die Paketdateien. Kommandos, die ursprünglich definiert wurden, werden in die Batch-Datei commandName.cmd gepackt.

Der eigentliche Erstellungsvorgang für Pakete wird folgendermaßen ausgelöst:

dotnet pack

Falls Assemblies erstellt werden sollen, wird dieses Kommando benutzt:

dotnet build

Das Werkzeug zum Ausführen ist dotnet – Die Ausführungsumgebung. Starten Sie bei der gezeigten Definition den integrierten Webserver wie folgt:

dotnet run

Sollte der Code nicht kompiliert worden sein, wird implizit dotnet build aufgerufen.

Falls dnx nicht im aktuellen Projektverzeichnis ausgeführt wird, kann der Pfad zur package.json angegeben werden:

dotnet <Pfad/nach/project.json> <Kommando>

Kommandos können auch als Nuget-Paket bereitgestellt werden. Die Ausführung eines Kommandos in Form eines Assemblynamens mutet etwas seltsam an. Tatsächlich muss die angegebene Assembly einen Einsprungpunkt bieten. Dazu wird die Schnittstelle ILoader implementiert, nachdem die Ausführungsumgebung sucht. Der ILoader lädt die eigentliche Assembly anhand ihres Namens. Er ruft dann den Einsprungpunkt aus. Dies ist normaler, verwalteter Code.