Die .NET-Familie 2018 mit Ausblick auf 2019

30
Okt

.NET Core 2.1, 2.2 und 3.0 – neue Funktionen

Bisher war .NET Core für viele Szenarien kein Thema, einfach weil die notwendigen Klassen fehlten. In .NET Core 2.1 hat Microsoft massiv in der Klassenbibliothek nachgerüstet. Mit .NET Core 3.0 kommen sogar WPF und Windows Forms nach .NET Core – aber nur für Windows. Was das im Detail bedeutet, wird im Folgenden erklärt.

Nachdem .NET Core 2.0 am 14. August 2017 erschienen war, hatte Microsoft die Version 2.1 für das erste Quartal 2018 angekündigt. Dass dieser Termin nicht zu halten sein wird, wurde dann im Februar 2018 verkündet und als neues Ziel der Sommer 2018 angegeben. Aus dem Sommer wurde dann der Frühsommer: .NET Core 2.1 und damit einhergehend ASP.NET Core 2.1 und Entity Framework 2.1 sind am 30. Mai 2018 erschienen.

.NET-Famework & C# Track auf der BASTA! Spring 2019

Windows Compatibility Pack for .NET Core

Ein wesentliches Thema in den 2.1-Versionen ist die Erweiterung der Funktionen, insbesondere durch die Übernahme weiterer Klassen aus dem klassischen .NET Framework. Das Windows Compatibility Pack for .NET Core bringt viele Hundert Klassen aus der bisherigen .NET Framework Class Library als Nuget-Paket in die .NET-Core-Welt. Das Nuget-Paket Microsoft.Windows.Compatibility ist ein Metapaket über viele andere Nuget-Pakete. Die Paketnamen sind angelehnt an die bisherigen Namensräume aus der .NET Framework Class Library. Funktionen wie Registry-Zugriff, Datenbankzugriffe per ODBC, LINQ für DataSets, das Code Document Object Model (CodeDOM), Zugriff auf serielle Ports und LDAP-Server wie das Active Directory sowie die Windows Management Instrumentation (WMI), mit der man Informationen aus dem Betriebssystem und einigen Anwendungen auslesen und verändern kann, halten damit Einzug in die Welt von .NET Core. Freilich sind viele dieser Klassen nur auf Windows möglich, daher auch der Name „Windows“ im Compatibility Pack.

Man kann in jedem Fall eine Self-contained Application (Abb. 1) auch für Linux und macOS erstellen, auch wenn manche Klassen aus dem Windows Compatibility Pack, die für diese Betriebssysteme nicht von Microsoft implementiert wurden bzw. dort zum Teil keinen Sinn machen (z. B. die Registry-Klassen). Wenn man die Self-contained Application dann auf diesen Betriebssystemen startet, erhält man Laufzeitfehlermeldungen wie z. B.:

  • System.PlatformNotSupportedException: Windows Principal functionality is not supported on this platform.
  • System.TypeInitializationException: The type initializer for ‚Microsoft.Win32.Registry‘ threw an exception. —> System.PlatformNotSupportedException: Registry is not supported on this platform.
  • System.PlatformNotSupportedException: System.Management currently is only supported for Windows desktop applications.

 

Abb. 1: Erstellen einer Self-contained App für Linux für eine Konsolenanwendung, die Linux verwendet

 

„Man sieht hier an den unterschiedlichen Fehlerklassen und Meldungstexten deutlich, dass verschiedene Entwickler am Werk waren. Einige Klassen aus dem Windows Compatibility Pack laufen auch außerhalb von Windows. Leider schweigt sich die offizielle Dokumentation dazu aus; es gibt jedoch dazu einen Blogeintrag.

So läuft zum Beispiel System.Runtime.Caching problemlos unter Linux und macOS, denn diese Bibliothek hat keine Abhängigkeiten von Betriebssystemteilen. Im Einzelfall wird man dann aber feststellen, dass mehr notwendig ist, als das Windows Compatibility Pack in die Self-contained Application zu packen. So scheitert ein ODBC-Zugriff mit der .NET-Klasse System.Data.Odbc.OdbcConnection auf Linux auf einen Microsoft-SQL-Server mit dem Fehler „System.DllNotFoundException: Dependency unixODBC with minimum version 2.3.1 is required. Unable to load shared library ‚libodbc.so.2‘ or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: liblibodbc.so.2.so: cannot open shared object file: No such file or directory”. Die Lösung ist, dass man erstmal den ODBC-Treiber für Microsoft-SQL-Server auf Linux installieren muss..

Auch für System.Drawing braucht man unter Linux und macOS eine Zusatzinstallation von libgdiplus, einer Open-Source-Implementierung des GDI+ API aus dem Mono-Projekt.

Mit dem NuGet-Paket Microsoft.DotNet.Analyzers.Compatibility bietet Microsoft immerhin einen Visual-Studio-Analyzer an, der alle Klassen und Klassenmitglieder markiert, die nicht plattformneutral sind, siehe z. B. Registry.LocalMachine.OpenSubKey in Abbildung 2. Auch auf veraltete APIs (z. B. System.Net.WebClient, System.Security.SecureString) weist der Analyzer hin. Der Analyzer ist aber leider auch schon wieder etwas verwahrlost. Die aktuelle Version ist 0.2.12-alpha und ist am 7. März 2018 erschienen.

 

Abb. 2: .NET Framework vs. .NET Core: Neu ab .NET Core 3.0 sind WPF und Windows Forms

 

Abb. 3: Meldung des Visual-Studio-Analyzers „Microsoft.DotNet.Analyzers.Compatibility“

 

Das Windows Compatibility Pack trägt in der ersten Version die Versionsnummer 2.0, was ausdrücken soll: Es läuft nicht nur auf .NET Core 2.1, sondern auch auf 2.0. .NET Core 2.0 sollte man allerdings nun gar nicht mehr verwenden (siehe Kasten „Support für .NET Core 2.0 läuft schon am 1.10.2018 aus!“).

Technisch ist das Windows Compatibility Pack eine Schicht oberhalb von .NET Standard 2.0 (Abb. 3). Daher kann das Windows Compatibility Pack nicht nur in .NET-Core-Projekten, sondern auch in Projekten mit dem Projekttyp „.NET Standard 2.0“ referenziert werden.

Neues in .NET Core 2.1

Neben Klassen aus dem klassischen .NET Framework hat Microsoft auch ganz neue Klassen mit .NET Core 2.1 eingeführt. Dazu gehören die Datenstrukturen Span, ReadOnlySpan, Memory und ReadOnlyMemory, die ab C# 7.2 ein direktes Adressieren von Teilen von Arrays und anderen Speicherbereichen ermöglichen. Neu ist auch die Brotli-Komprimierung nach RFC 7932 im Paket System.IO.Compression.Brotli. Bei den Kryptografie-APIs gibt es viele kleine Neuerungen.

Auch an der Runtime hat Microsoft gefeilt. .NET Core 2.1 kann schneller kompilieren (sowohl im Sprachcompiler zur Entwicklungszeit als auch zur Laufzeit im Just-in-Time-Compiler). Ebenso die Netzwerkbibliotheken (u. a. die Klasse System.Net.Http­Client) hat das Entwicklungsteam optimiert. Die Tiered Compilation, durch die der Just-in-Time-Compiler zunächst den Fokus auf schnelle Übersetzung statt optimalem Ergebnis legt und dann für häufiger verwendete Methoden die Übersetzung nachträglich optimiert, ist in .NET Core 2.1 aber nur in einer Vorschauversion enthalten. Diese Funktion muss der Anwendungsbetreiber explizit durch das Setzen einer Umgebungsvariablen aktivieren: COMPlus_TieredCompilation=“1″. Unglaublich, dass „COMPlus“ als Name auch siebzehn Jahre nach dem Erscheinen der ersten .NET-Version noch weiterlebt.

ASP.NET Core 2.1

In ASP.NET Core 2.1 gibt es einige Neuerungen für Web-APIs: Mit der neuen Annotation [ApiController] auf Ebene der Controllerklassen gibt es mehr Automatismen bezüglich Parameterbindung und Validierung. So sucht der Model Binder nun komplexe Typen – wie früher im klassischen ASP.NET-Web-API – im Inhalt der HTTP-Nachricht statt im Querystring. Wenn Validierungen fehlschlagen und dadurch der Model State ungültig ist, liefert der Controller jetzt automatisch den HTTP-Statuscode 400 (Bad Request) an den Aufrufer.

Mit dem neuen Rückgabetyp ActionResult kann der Entwickler HTTP-Statuscodes setzen und zur gleichen Zeit ein typisiertes Objekt für den Inhalt der Antwort festlegen, was die Generierung von Swagger-OpenAPI-Dokumentationen vereinfacht. Bei der Verwendung der untypisierten Schnittstelle IActionResult musste der Entwickler bisher Swagger durch eine Annotation über den Ergebnistyp informieren. Microsoft unterstützt nun auch die Übermittlung von Problemdetails in Web-APIs nach RFC 7808.

In ASP.NET Core 2.1 hat Microsoft Areas nachgerüstet, um Teilbereiche innerhalb eines Projekts zu trennen – das gab es schon im alten ASP.NET MVC. Ein Einsatz für Areas in ASP.NET Core sind die Themengebiete Benutzeranmeldung und Benutzerverwaltung beim Server-Side-Rendering nach dem Model-View-Controller-Modell oder per Razor Pages. Wenn man in Visual Studio 2017 ab Version 15.7 ein neues Webprojekt für ASP.NET 2.1 anlegt, findet man im Ordner Area einen Unterordner Identity. Dort ist zunächst nur eine einzelne Datei _ViewStart.cshtml hinterlegt. Sowohl die Seitenlayouts als auch den zugehörigen Programmcode findet man in der Assembly Microsoft.AspNetCore.Identity.UI.dll. Microsoft kapselt diese Funktionen, man kann eine einfache Anpassung über die Wahl der Layoutseite in _ViewStart.cshtml vornehmen. Wem das nicht reicht, der kann sich den Quellcode des o. g. Assembly über die Funktion Add Scaffold/Identity in Visual Studio ausgeben lassen. Wenn man sich die dann generierten Seiten ansieht, fällt auf: Es sind Razor Pages, selbst wenn man als Projektvorlage nicht Razor Pages, sondern Model-View-Controller gewählt hat. Microsoft hatte ja schon bei ASP.NET 2.0 klargemacht, dass Razor Pages nun das bevorzugte Modell für Server-Side-Rendering sind.

Für Razor Pages führt Microsoft weitere Neuerungen ein: vereinfachte Datenbindung in Razor mit der Annotation [BindPropertyAttribute] zentral auf eine Page-Model-Klasse statt auf einzelne Properties, und mit der Schnittstelle IPageFilter können Entwickler eigene Logik nun vor und nach dem Razor Page Handler ausführen. Immerhin auch für MVC-Projekte gilt: Die Projektvorlagen berücksichtigen die EU-GDPR (in Deutschland: DSGVO) und fordern nun vom Benutzer das Einverständnis für Cookies ein. Wenn der Benutzer nicht zustimmt, werden automatisch alle Cookies unterdrückt, die nicht als Essential markiert sind.

ASP.NET SignalR für bidirektionale Kommunikation zwischen Webserver und Browser ist nun in einer Core-Version 1.0 als Zusatz zu ASP.NET Core 2.1 verfügbar. Auf später verschoben wurden aber die WebHooks ebenso wie die schnellere In-Process-Integration in Internet Information Services (IIS).

Entity Framework Core 2.1

Microsofts überarbeiteter OR-Mapper macht in Version 2.1 einen größeren Schritt nach vorne, insbesondere wurden vier Funktionen nachgerüstet, die es im klassischen ADO.NET Entity Framework schon gab:

  • Lazy Loading (wahlweise mit Runtime Proxies oder Ergänzungen der Entitätsklassen; anders als im ­ADO. NET Entity Framework ist Lazy Loading eine Option und nicht mehr automatisch aktiv)
  • Übersetzung des LINQ-Operators GroupBy() in SQL (in Version 1.x und 2.0 wurden tatsächlich alle GroupBy()-Operationen im RAM ausgeführt, was bei großen Datenmengen zu indiskutablen Ausführungsgeschwindigkeiten führt. In Version 2.1 werden einige, aber noch immer nicht alle GroupBy()-Fälle in SQL übersetzt)
  • ambiente Transaktionen mit System.Transactions.TransactionScope
  • Data Seeding im Rahmen von Schemamigrationen

Darüber hinaus enthält Entity Framework Core auch einige ganz neue Funktionen, die es nicht im klassischen ADO.NET Entity Framework gab:

  • Wertkonvertierung beim Materialisieren und Speichern von Objekten mit Value-Converter-Funktionen
  • Entitätsklassen dürfen nun Parameter im Konstruktor besitzen
  • Unterstützung für Owned Types mit der Annotation [Owned]
  • Zustandsänderungsereignisse im Change Tracker

Es bleibt aber dabei, dass es Schwächen in Entity Framework Core gibt. Der LINQ-Befehl in Listing 1 wird auch in Entity Framework Core Version 2.1 nicht komplett in SQL übersetzt. Wie Listing 2 zeigt, fehlen Gruppierung und Aggregatoperatoren im SQL-Befehl. Der ORM liest hier weiterhin alle Datensätze aus der Tabelle und macht den Rest im RAM. Von Microsoft liest man dazu: „We now support translating it to the SQL GROUP BY clause in most common cases“. Schade, dass so ein einfacher LINQ-Befehl wie der in Listing 1 nicht dazugehört.

Auch fehlen in Entity Framework Core 2.1 weiterhin die Unterstützung für das Table-per-Hierarchy-(TPH-)Mapping, die Abstraktion von der Zwischentabelle beim N:M-Mapping und Validierung vor dem Speichern. Auch ein Update Model from Database gibt es nicht für den vom Reverse Engineering generierten Programmcode, wenn sich das Datenbankschema danach noch ändert.

Listing 1: LINQ-Befehl mit GroupBy()-Operator

var groups1 = (from p in ctx.FlightSet
               group p by p.Departure into g
               select new { City = g.Key, Min = g.Min(x => x.FreeSeats), Max = g.Max(x => x.FreeSeats), Sum = g.Sum(x => x.FreeSeats), Avg = g.Average(x => x.FreeSeats) });

Listing 2: SQL-Übersetzung von Listing 1 – die Gruppierung fehlt

SELECT [p].[FlightNo], [p].[AircraftTypeID], [p].[AirlineCode], [p].[CopilotId], [p].[FlightDate], [p].[Departure], [p].[Destination], [p].[FreeSeats], [p].[LastChange], [p].[Memo], [p].[NonSmokingFlight], [p].[PilotId], [p].[Price], [p].[Seats], [p].[Strikebound], [p].[Timestamp], [p].[Utilization]
FROM [Flight] AS [p]
ORDER BY [p].[Departure]

 

Version 2.2 soll Ende 2018 erscheinen

Zwischen Version 2.0 und 2.1 gab es sehr viele Neuerungen – mehr als zwischen 1.1 und 2.0. Gemäß Semantic Versioning ist die Änderung der ersten Versionsnummer kein Indikator für den Umfang der Neuerungen, sondern drückt nur aus, dass es Breaking Changes gab. Version 2.2 ist funktional wieder ein kleineres Release, das bis Jahresende 2018 erscheinen soll.

In ASP.Net Core 2.0 will Microsoft das Routing überarbeiten. Das neue Routingsystem (derzeit „Dispatcher“ genannt) soll sehr früh in der Verarbeitungspipeline laufen und einige Szenarien wie die Unterstützung von CORS vereinfachen. Für Web-APIs soll die Generierung von Swagger-OpenAPI-Dokumenten und Hilfeseiten abermals durch Konventionen vereinfacht werden, z. B. soll ein Verb wie Create zu Beginn des Namens einer Action-Methode automatisch dazu führen, dass der Rückgabestatuscode 201 ist.

Bei den Werkzeugen will Microsoft die Analyse von Web-API-Konventionen in Visual Studio ermöglichen. Das Kommandozeilenwerkzeug dotnet soll beim Test HTTP-Anfragen mit beliebigen HTTP-Verben unterstützen. Sowohl für die Kommandozeile als auch Visual Studio will Microsoft einen eigenen Codegenerator für Clientzugriffscode (in C# und TypeScript) für Web-APIs anbieten. SignalR soll Unterstützung für Clients in Java und C++ erhalten. Der Kestrel-Webserver soll HTTP/2 und Health Checks zur Überwachung unterstützen.

Entity Framework Core 2.2 soll beim Reverse Engineering bestehender Datenbanken nun auch Datenbank-Views generieren. Aus dem alten ADO.NET Entity Framework soll auch die Unterstützung für Spatial Types (geometry und geography) übernommen werden. Die in Version 2.0 eingeführten Owned Types sollen in Version 2.2 auch bei Collection-Navigationseigenschaften möglich sein. Bisher sind sie nur für Einzelobjekte erlaubt. Der schon für Version 2.1 geplante, aber dann verschobene Treiber für Azure Cosmos DB soll erscheinen. Das Entwicklungsteam hat außerdem versprochen, mehr Unittests zu schreiben – und damit weniger Fehler zu produzieren, die erst den Kunden auffallen.

 

Mit Version 3.0 kommt .NET Core auf den Desktop

Bisher konnte man mit .NET Core nur Web-APIs, Webanwendungen und Konsolenanwendungen sowie Universal-Windows-Platform-Apps erstellen. Entwickler klassischer Desktopanwendungen schauten in die Röhre. Microsoft hat auf der BUILD-Konferenz 2018 verkündet, dass die Windows Presentation Foundation (WPF) und sogar die „alte Tante“ Windows Forms im Rahmen von Version Core 3.0 auf .NET Core portiert werden. Sie laufen dann als Nuget-Pakete, allerdings nur auf Windows. Weder WPF noch Windows Forms werden durch diese Maßnahme plattformunabhängig. Dazu müsste Microsoft nicht nur den .NET-Teil beider Frameworks, sondern auch die Betriebssystemgrundlagentechniken GDI+ bzw. DirectX portieren. Zumindest GDI+ gibt es ja, denn es gibt in Mono auch Windows Forms.

Immerhin können mit WPF und Windows Forms auf .NET Core aber Softwarehersteller, die nicht an Plattformneutralität interessiert sind, ihre bestehenden Windows-Anwendungen auf .NET Core auf Windows (ab Windows 7 bzw. Server 2008 R2 – auf älteren Windows-Versionen läuft .NET Core heute nicht!) betreiben und damit von den Vorteilen profitieren, die .NET Core im Bereich der Werkzeuge, der Geschwindigkeit und der Softwareinstallation bietet. Neben den schon existierenden Deployment-Optionen für .NET Core als Portable App oder Self-contained App will Microsoft einen neuen .NET Core App Bundler schreiben, der per Tree Shaking nicht notwendigen Programmcode (Dead Code) aus der Intermediate Language herauszieht. Am Ende entsteht – wie bei UWP-Apps – nur noch eine ausführbare Datei, die nur den tatsächlich verwendeten Programmcode enthält.

WPF- und Windows-Forms-Anwendungen müssen für .NET Core 3.0 neu kompiliert werden. Ob dazu Änderungen am Programmcode notwendig sein werden, hat Microsoft noch nicht klar dementiert. Aktuell gibt es noch keine öffentliche Previewversion von .NET Core 3.0. Eine fertige Version soll im ersten Quartal 2019 erscheinen. Man kann an diesem Zeitplan durchaus Zweifel haben. Microsoft will bei der Gelegenheit das klassische ADO.NET Entity Framework umschreiben (Abb. 2), sodass es auf .NET Core 3.0 auf Windows läuft, da viele Desktopanwendungen darauf basieren. Angekündigt ist auch eine Erweiterung des .NET-Standards im Rahmen von .NET Core 3.0.

Anfang August hat Microsoft den .NET Portability Analyzer aktualisiert, sodass dieser nun schon die für .NET Core 3.0 geplanten Klassen berücksichtigt. .NET-Entwickler können somit schon sehen, ob ihre WPF- und Windows-Forms-Anwendungen unter .NET Core 3.0 laufen werden. Microsoft kann mit dem Werkzeug sehen, welche Klassen bei Kunden im Einsatz sind und ggf. die Entscheidung für die Klassen in .NET Core 3.0 auf dieser Basis ändern. Anders als zunächst angekündigt, läuft .NET Portability Analyzer aber noch nicht auf einem Prototyp von .NET Core 3.0, sondern auf dem klassischen .NET Framework.

 

UWP-Inseln

Eine weitere Ankündigung in diesem Zusammenhang sind die UWP-XAML-Inseln. Damit können Entwickler UWP-Steuerelemente in WPF- und Windows-Forms-Fenster einbinden. Das setzt dann noch engere Grenzen, denn UWP gibt es nur auf Windows 10. Wer den Luxus hat, nur noch für Windows 10 entwickeln zu können, kann dann Steuerelemente wie das Edge Browser Control oder das UWP-Stifteingabesteuerelement in seine alten GUIs einbinden. Das soll einfach per Drag-and-drop aus der Visual-Studio-Werkzeugleiste gehen. Diese UWP-XAML-Inseln will Microsoft nicht nur in .NET Core 3.0, sondern in der kommenden Version 4.8 des klassischen .NET Framework erlauben.

 

Fazit

Es tut sich eine Menge bei .NET Core und das ist auch gut so. Es ist wichtig, dass Microsoft baldmöglichst allen Entwicklern ermöglicht, auf .NET Core zu setzen, damit man spätestens 2019 keine neuen Projekte mehr im klassischen .NET Framework beginnen muss.

Natürlich gab es nach den Ankündigungen von Windows Compability Pack, Windows Forms und WPF auf .NET Core nur für Windows sofort Skeptiker, die aufschrien, dass Microsoft die Plattformunabhängigkeit von .NET Core beerdige. Nein, das tun sie nicht.

ASP.NET Core und Konsolenanwendungen laufen auch weiterhin plattformunabhängig auf Windows, Linux und macOS. Microsoft dehnt nur die Einsatzszenarien für .NET Core auf die Entwickler aus, die weiterhin nur für Windows entwickeln können oder wollen. Und auch das ist – ich wiederhole mich – gut so!

 

 

Ihr aktueller Zugang zur .NET- und Microsoft-Welt.
Der BASTA! Newsletter:

Behind the Tracks

.NET Framework & C#
Visual Studio, TFS, C# & mehr

Agile & DevOps
Best Practices & mehr

Web Development
Alle Wege führen ins Web

Data Access & Storage
Alles rund um´s Thema Data

HTML5 & JavaScript
Leichtegewichtig entwickeln

User Interface
Alles rund um UI- und UX-Aspekte

Microservices & APIs
Services, die sich über APIs via REST und JavaScript nutzen lassen

Security
Tools und Methoden für sicherere Applikationen

Cloud, Azure, Serverless
Cloud-basierte & Native Apps