Eine (subjektive) Auswahl der Neuheiten

Fünf Highlights im .NET Framework 4.5
Kommentare

Bei allen Neuerungen im .NET Framework 4.5 ist es schwer einen vollständigen Überblick zu schaffen. Dieser Artikel zeigt daher eine Top 5 der wesentlichen neuen Möglichkeiten. Und da eine Auswahl sowieso immer subjektiv ist, kann man dies unumwunden als die persönliche Hitliste des Autors (aka Dotnet-Doktor) bezeichnen.

Nach Art eines Countdown beginnt diese Hitliste meiner persönlichen Highlights der Neuerungen, die mit dem .NET Framework 4.5 Einzug halten, mit dem letzten Platz:

Platz 5: WinRT Library auch ohne Windows-8-App

Mit dem neuen Windows-Runtime-(WinRT-)API verbinden viele Entwickler immer nur Windows-8-Apps (früher „Metro-Style-Apps“ genannt), dabei kann man WinRT auch in „klassischen“, mit .NET geschriebenen Desktopanwendungen nutzen. WinRT basiert auf einer erweiterten Fassung des Component Object Model (COM) und .NET 4.5 bietet einige Brücken zu diesem neuen COM.

Vorteil: In den .NET-Anwendungen stehen dann auch die systemnahen WinRT-Klassen (z. B. für Sensoren, Bluetooth und Windows Search) zur Verfügung. Der Entwickler kann hier viele Hundert Klassen nutzen, für die es bisher nur umständliche Funktionsaufrufe im Win32-API gab.

Nachteil: Solche Anwendungen laufen dann auch nur unter Windows 8 – zumindest solange nicht Microsoft seine Meinung noch irgendwann ändert und WinRT doch auch für ältere Betriebssysteme anbietet. Zudem kann man nur die nicht-visuellen WinRT-Klassen nutzen. Die Native-Code-basierte XAML-Implementierung ist bisher nicht zugreifbar aus klassischen .NET-Anwendungen.

Um WinRT in einer klassischen Desktopanwendung zu nutzen, muss man in einer .NET-Anwendung (z. B. einer Konsolenanwendung, einer Windows-Forms-Anwendung oder einer WPF-Anwendung) eine Referenz auf das WinRT-API setzen. Im Standard erscheint im Add-Reference-Dialog aber kein Eintrag „Windows Runtime“, selbst auf einem Windows-8-System nicht.

Um diese Referenz zu erlauben, gibt es einen Trick: Man muss die Projektdatei (.csproj bzw. .vbproj) manuell bearbeiten. In einem beliebigen Texteditor ergänzt man in dem Tag folgenden Eintrag:

 8.0

Danach lädt man das Projekt neu in Visual Studio und findet dann im Dialog Add Reference einen neuen Eintrag Windows. Dort gibt es auch nur eine Komponente Windows mit der Versionsnummer 255.255.255.255 (Abb.1). Durch Anwählen dieser Komponente stehen alle Referenzen für normale .NET-Anwendungen nun zur Verfügung.

Abb. 1: Referenzierung des Windows-Runtime-API in einem klassischen .NET-Projekt

Das reicht aber noch nicht: In den meisten Fällen bei der Arbeit mit dem WinRT-API ist auch eine Referenz auf eine oder mehrere Fassaden-Assemblies notwendig. Diese enthalten die Abbildungen von WinRT-Klassen auf .NET-Klassen. Man findet sie unter: C:Program Files (x86)Reference AssembliesMicrosoftFramework.NETFrameworkv4.5Facades.

Für den Aufruf asynchroner Operationen in WinRT benötigt .NET zudem einige Hilfsklassen, die sich in der System.Runtime.WindowsRuntime.dll (siehe C:WindowsMicrosoft.NETFramework64v4.0.30319) befinden. Da die meisten Operationen in WinRT asynchron sind, ist also diese Referenz fast immer notwendig.

Listing 1 zeigt eine Dateisuche mit Windows (Desktop) Search, implementiert in .NET 4.5 mit C# 5.0 in einer Konsolenanwendung und unter Verwendung der WinRT-Klassen im Namensraum Windows.Storage.Search und Windows.Storage. Notwendige Fassaden-Assembly ist die System.Runtime.dll.

Listing 1: Windows-Suche mit WinRT
 static void Main(string[] args)           {               Console.WriteLine("Starte");               DateiSuchDemo("Mountain Bike", @"w:dokumente");               Console.WriteLine("Warte...");               while (true) { Console.Write("."); System.Threading.Thread.Sleep(100); }           }             ///            /// Suche ausführen mit Windows Search auf Basis der WinRT-Klassen im Namensraum Windows.Storage.Search           ///            private static async void DateiSuchDemo(string Begriff, string Ordner)           {               // Suche definieren               var docs = Windows.Storage.KnownFolders.DocumentsLibrary;               var queryOptions = new Windows.Storage.Search.QueryOptions();               queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep;               queryOptions.IndexerOption = Windows.Storage.Search.IndexerOption.UseIndexerWhenAvailable;               queryOptions.UserSearchFilter = "'" + Begriff + @"' folder:" + Ordner; // Advanced Query Syntax (AQS)               var query = docs.CreateFileQueryWithOptions(queryOptions);                 // Suche starten               Console.WriteLine("Windows Suche im Ordner " + Ordner + " nach Dateien mit Begriff: " + Begriff);               Console.WriteLine("Windows Suche beginnt...");                 IReadOnlyList ergebnis = await query.GetFilesAsync();                 // Ergebnisse zeigen               Console.WriteLine("nSuche abgeschlossen. Gefundene Dateien: " + ergebnis.Count);               Console.WriteLine("--------------------------------------------------------");               foreach (Windows.Storage.StorageFile f in ergebnis)               {                   Console.WriteLine(f.Name + " ("  + f.FileType + "): " + f.Path);               }           } 
Platz 4: ASP.NET Bundling und Minification

Websites bestehen heutzutage häufig aus zahlreichen größeren CSS- und JavaScript-Dateien. Gerade beim Laden über langsame Netzwerkverbindungen (z. B. in Mobilfunknetzwerken) wird dies zum Problem. ASP.NET 4.5 bietet zwei Instrumente zur Beschleunigung:

  • CSS- und JavaScript-Dateien können vor der Übertragung zum Client verkleinert werden, indem Leerräume und Kommentare aus dem Quellcode entfernt werden (Abb.2).
  • Mehrere CSS- und JavaScript-Dateien können vor der Übertragung zu einer Datei zusammengefasst werden. Durch die Reduzierung der Anzahl der HTTP-Anfragen wird die Übertragung einer Webseite beschleunigt.

 Abb. 2: Zusammenfassung und Verkleinerung mehrerer CSS-Dateien zu einer Datei

Microsoft spricht von Minification und Bundling. Beide Funktionen sind realisiert in der System.Web.Optimization.dll, die den gleichnamigen Namensraum enthält. Trotz des Namens gehört diese Assembly nicht zum Kern des .NET Frameworks, sondern ist ein Erweiterungsprojekt, das Microsoft über NuGet verbreitet [1]. Allerdings wird die System.Web.Optimization.dll mit Visual Studio 2012 ausgeliefert und gehört mit zur Standardvorlage für neue erzeugt ASP.NET-Anwendungen.

Es gibt so genannte „Standard-Bundles“ mit Namen /js und /css. Wenn man /js an einen Verzeichnisname anhängt, werden alle in dem Verzeichnis enthaltenen JavaScript-Dateien verbunden und verkleinert. Gleiches gilt mit /css für alle CSS-Dateien in einem Ordner. Die Standard-Bundles muss man in der global.asax-Datei aktivieren:

 void Application_Start(object sender, EventArgs e)    {
System.Web.Optimization.BundleTable.Bundles.EnableDefaultBundles();   } 

Danach kann man, statt wie in Listing 2, vereinfacht wie in Listing 3 schreiben.

Listing 2
                      HTML/CSS Testseite        
                    HTML/CSS Testseite           

Im Rahmen der Standard-Bundles werden die Dateien alphabetisch sortiert. Allerdings gibt es Ausnahmen: Sofern im Verzeichnis die Standard-JavaScript-Bibliotheken jQuery, Dojo oder MooTools enthalten sind, werden diese zuerst geladen. Dies ermöglicht, dass die folgenden Skripte diese verwenden. Bei CSS-Dateien werden die möglicherweise vorhandenen Dateien reset.css und normalize.css zuerst geladen. Alternativ kann man in der global.asax ein ScriptBundle oder StyleBundle manuell definieren mit einer spezifischen Reihenfolge. Das individuelle Bundle muss in der Klasse System.Web.Optimization.BundleTable.Bundles mit Add() registriert werden.

[ header = Fünf Highlights im .NET Framework 4.5 – Teil 2 ]

Platz 3: ASP.NET Model Binding

Zusätzlich zu den bisherigen Datenbindungsverfahren in ASP.NET (DataSource-Property und DataSource-Steuerelemente wie ObjectDataSource) bietet ASP.NET 4.5 nun auch „Model Binding“, das Microsoft zuvor in ASP.NET MVC eingeführt hatte.

Anders als bei dem ObjectDataSource-Steuerelement gibt es bei dem Model Binding keinen Intermediär zwischen Datensteuerelement und dem Programmcode. Vielmehr hat Microsoft die Datensteuerelemente um bis zu vier Eigenschaften erweitert, die jeweils auf den Namen einer Methode in der Code-Behind-Datei verweisen können:

  • SelectMethod: Name einer Methode, die Objekte liefert in Form einer aufzählbaren Menge als IEnumerable, IQueryable, IEnumerable oder IQueryable. Sie kann ggf. Parameter für Filtern und Sortieren besitzen.
  • InsertMethod: Name einer Methode, die ein einzufügendes Objekt als Parameter empfängt.
  • DeleteMethod: Name einer Methode, die ein löschendes Objekt als Parameter empfängt.
  • UpdateMethod: Name einer Methode, die ein aktualisierendes Objekt als Parameter empfängt.

Zum Beispiel kann man diese Methoden im GridView-Steuerelement verwenden, wobei das GridView von Microsoft ja bekanntlich kein Datensatzeinfügen unterstützt, also nur drei der vier Methoden möglich sind (Listing 4).

Listing 4
      ...       

In der Code-Behind-Datei realisiert man dann die Methoden dann z. B. mit dem ADO.NET Entity Framework (Listing 5). Die im Datensteuerelemente genannten Methoden müssen leider direkt in der Code-Behind-Datei realisiert werden. Es ist nicht möglich, im Datensteuerelement auf eine externe Klasse zu verweisen. Allerdings ist es natürlich nicht zwingend notwendig, den Datenzugriffscode direkt in der Code-Behind-Klasse zu realisieren. Die Model-Binding-Methoden können selbstverständlich an den Aufruf an eine darunterliegende Schicht (z. B. Geschäftslogik oder Web Services) weiterleiten.

Listing 5: Datenzugriffscode für das ASP.NET Model Binding
WWWings6Entities model = new WWWings6Entities();         // Select-Methode für GridView: Holt alle Flüge       public IQueryable GetFlug()       {          return model.Flug;       }         // Update-Methode für GridView       public void UpdateFlug(Flug flug)       {        if (ModelState.IsValid)        {         model.AttachTo("Flug", flug);         model.ObjectStateManager.ChangeObjectState(flug, System.Data.EntityState.Modified);         model.SaveChanges();        }        }         // Delete-Methode für GridView       public void DeleteFlug(Flug flug)       {        if (ModelState.IsValid)        {         model.AttachTo("Flug", flug);         model.ObjectStateManager.ChangeObjectState(flug, System.Data.EntityState.Deleted);         model.SaveChanges();        }       }  

Die Select-Methode kann Filterparameter besitzen. Diese Filterparameter können mit Werten befüllt werden, wenn das Datensteuerelement die SelectMethod aufruft. Dafür gibt es beim Model Binding so genannte „Value Provider“, die die Werte aus verschiedenen Quellen akquirieren: Control, Cookie, Form, QueryString, Profile, ViewState, Session. Man kann auch eigene Value Provider schreiben (Basisklasse ValueProvider).

Der Value Provider hat die Aufgabe, den Wert aus der jeweiligen Quelle zu extrahieren. Wenn das nicht gelingt, muss er null übergeben. Daher sollten die Parameter einen wertelosen („nullable“) Datentyp besitzen. Der zu verwendende Value Provider wird vor einem Methodenparameter als .NET-Attribut deklariert. Die Bindung an die Datenquelle erfolgt gemäß Konvention (Namensübereinstimmung):

public IQueryable GetFluege([System.Web.ModelBinding.Control] string Ort) 

oder Konfiguration (explizite Angabe):

public IQueryable GetFluege([System.Web.ModelBinding.QueryString(“City”)] string Ort)

Außerdem beachtet das Model Binding die Validierungsannotationen, die man im gebundenen Objekttyp mit den in .NET 3.5 SP1 eingeführten System.ComponentModel.DataAnnotations hinterlegen kann. Ein ValidationSummary-Steuerelement und die Prüfung IsValid() bei Update und Insert reichen dann, um zu verhindern, dass nicht gültige Daten gespeichert werden.

Platz 2: Mapping für Enumerationen, Geo-Datentypen und Table Valued Functions in ADO.NET Entity Framework

Das ADO.NET Entity Framework 5.0 bietet nun endlich das bisher sehr vermisste Mapping für Aufzählungstypen, Geo-Datentypen Geography und Geometry sowie Table Valued Functions (TVFs).

Ein Attribut einer Entität, das einen Ganzzahlenwert als Datentyp hat, kann man mit Convert To Enum im Designer auf eine Enumeration abbilden. Die Werte für die Enumeration kann man über eine grafische Eingabemaske pflegen (Abb. 3). Die Enumerationen erscheinen im Model-Browser. Die Codegenerierung erzeugt einen Enum-Typ mit [DataContractAttribute], der dann im Programmcode nutzbar ist (Listing 6).

Abb. 3: Enumerationen im Entity Framework definieren in Visual Studio 2012

Listing 6 zeigt ebenso die Verwendung des geografischen SQL-Server-Datentypen Geometry. Das Mapping für solche Geo-Datenspalten erfolgt jetzt durch den Reserve-Engineering-Assistent in Visual Studio 2012 automatisch auf die Klassen DbGeometry und DbGeography im neuen Namensraum System.Data.Spatial. Beim Erstellen eines Objekts des Typs DbGeometry oder DbGeography stellt man fest, dass es keine Konstruktoren gibt. Stattdessen verwendet man die diverse statische Methode wie Parse(), FromGml(), FromText(), LineFromBinary(), PolygonFromText(), u.v.m. die Daten als Well-Known Text (WKT) oder Well-Known Binary (WKB) oder Geography Markup Language (GML) erwarten.

Listing 6: Windows-Suche mit WinRT
WWWings6Entities modell = new WWWings6Entities();      // Objekt anlegen      var f = modell.Flughafen.CreateObject();      // Key zuweisen      f.Name = "Essen/Mülheim";      // Zuweisen eines Enumerationswertes      f.Typ =  Flughafenart.Hubflughafen;      // Setzen eines WGS84-Punktes (Flughafen Essen/Mülheim)      // Bei Eingabe in Textbox: SRID=4326;POINT(51.4023 6.9373)      f.Position = DbGeography.FromText("POINT(51.4023 6.9373)", 4326);      // Anfügen      modell.Flughafen.AddObject(f);      // Speichern      modell.SaveChanges(); 

Das ADO.NET Entity Framework 5.0 und der Visual Studio 2012 Entity Designer unterstützen aber nun auch TVFs. Der Entity-Framework-Reverse-Engineering-Assistent zeigt TVFs im Ast Stored Procedures and Functions an. Durch das Häckchen Imported selected Stored Procedures and Functions into the entity model wird jeweils automatisch ein so genannter „Function Import“ im Modell erzeugt und dazu passend jeweils eine Methode in der Entity-Framework-Kontextklasse generiert. Im Code kann man die TVF (hier als Beispiel: GetFluegeVon) dann verwenden wie eine Stored Procedure:

var fluege = (from f in modell.GetFluegeVon("Rom") where f.FreiePlaetze > 10 && f.Zielort == "Moskau" orderby f.Datum select f).Take(5).ToList(); 

Wenn GetFluegeVon eine Stored Procedure wäre, dann würde die Datenbank nur das Filtern nach dem Abflugort „Rom“ erledigen. Das Filtern nach Zielort und freien Plätzen sowie die Sortierung nach Datum würden im RAM erfolgen. Eine TVF bringt den großen Vorteil, dass die erweiterten Bedingungen und das Sortierkriterium in SQL-Form angewandelt und zur Datenbank übertragen werden, wo eine Verbindung mit der in der TVF hinterlegten Select-Bedingung stattfindet. In der Regel ist die Ausführungsgeschwindigkeit dann wesentlich höher.

[ header = Fünf Highlights im .NET Framework 4.5 – Teil 3 ]

Platz 1: Asynchrones Programmieren

Den größten Vereinfachungsschub durch .NET 4.5 erhält das asynchrone Programmieren. Seit .NET 1.0 gab es die Möglichkeit der asynchronen Programmierung mit Delegates (BeginInvoke(),AsyncCallback, EndInvoke(), IAsyncResult). In .NET 2.0 führte Microsoft zur Vereinfachung das Async Event Pattern ein, wo es zu asynchronen Methoden in der Klassenbibliothek jeweils ein Completed-Ereignis gab. In .NET 4.5 ist die asynchrone Programmierung nun mit den Schlüsselwörter async und await direkt in der Sprachsyntax von C# und Visual Basic .NET verankert.

Eine Methode kann mit dem neuen Schlüsselwort async (C#) bzw. Async (Visual Basic) deklarieren, dass sie in der Lage ist, im Laufe ihrer Ausführung die Kontrolle an den Aufrufer zurückzugeben. Eine solche asynchrone Methode gibt ein Task-Objekt (aus der in .NET 4.0 eingeführten Task Parallel Library (TPL)) zurück. Task ist eine generische Klasse, die als Typparameter einen beliebigen .NET-Typ erlaubt und an dieser Stelle den eigentlichen Rückgabetyp der Methode deklariert. Aus einer synchronen Methode public string GetXY() wird so eine asynchrone Methoden public async Task GetXYAsync(). Dabei ist das Anhängen des Wortes „Async“ aber lediglich eine freiwillige Konvention, keine Pflicht.

Innerhalb einer solchen asynchronen Methode wird die Kontrolle dann genau nach dem ebenfalls neuen Schlüsselwort Await bzw. await an den Aufrufer zurückgegeben. Await bezieht sich immer auf ein Task-Objekt, das man entweder selbst erzeugt hat oder von einer anderen asynchronen Methode stammt. Weiterhin kümmert sich das .NET Framework 4.5 nicht um die Erzeugung von Tasks – das muss die asynchrone Methode selbst tun. Aber .NET Framework 4.5 macht den Aufruf, insbesondere die Weiterverarbeitung nach einem asynchronen Aufruf radikal einfacher.

Microsoft hat viele Klassen in der .NET-Framework-Klassenbibliothek mit neuen asynchronen Methoden ausgestattet, die die bisherigen asynchronen Methoden ergänzen. Dazu gehört z. B.:

  • System.Data.Common.DbConnection (bzw. Davon abgeleitete Klassen wie SqlConnection): OpenAsync()
  • System.Data.Common.DbCommand (bzw. Davon abgeleitete Klassen wie SqlCommand): ExecuteReader()
  • Klasse System.IO.TextReader (bzw. davon abgeleitete Klassen wie StreamReader): ReadAsync(), ReadBlockAsync(), ReadLineAsync(), ReadToEndAsync()
  • Klasse System.IO.TextWriter (bzw. davon abgeleitete Klassen wie StreamWriter): WriteAsync(), WriteLineAsync(), FlushAsync()
  • System.Net.Webclient: OpenReadTaskAsync(), OpenWriteStringTaskAsync(), DownloadDataTaskAsync(), DownloadFileTaskAsync(), DownloadStringTaskAsync(), UploadDataTaskAsync(), UploadFileTaskAsync(), UploadStringTaskAsync() und UploadValuesTaskAsync()

Bei dem Namen der neuen Methoden in Webclient fällt auf, dass diese auf „TaskAsync“ enden, während in anderen FCL-Klassen der bisherige Name nur um „Async“ erweitert wurde. Grund für diese Abweichung ist, dass die Klasse WebClient bereits in .NET 2.0 asynchrone Methoden (z. B. DownloadStringAsync()) erhalten hat, die einem anderen Entwurfsmuster folgen: Sie lösen bei Fertigstellung ein Ereignis aus (z. B. DownloadStringCompleted()). Dadurch war der Name mit „Async“ am Ende schon belegt.

Listing 7 zeigt als Beispiel das asynchrone Auslesen einer Datei. Während es in der .NET-Framework-Klassenbibliothek nun also synchrone und asynchrone Operationen gleichberechtigt nebeneinander gibt, hat Microsoft beim Entwurf des Windows-Runtime-API von Anfang an die Maxime verfolgt, dass alle Operationen, die länger als 50 Millisekunden dauern könnten (!), nur als asynchrone Operationen zur Verfügung stehen dürfen.

Listing 7: Asynchrones Auslesen einer Datei
public  static void Run()     {      Console.WriteLine("Run() #1: Thread=" + System.Threading.Thread.CurrentThread.ManagedThreadId);      ReadAsync();      Console.WriteLine("Run() #2: Thread=" + System.Threading.Thread.CurrentThread.ManagedThreadId);     }       ///      /// Diese Methode startet das asynchrone Lesen     ///      ///      public async static Task ReadAsync()     {      string path = @"w:tempdaten.txt";      string inhalt = "";      using (var sr = new System.IO.StreamReader(path))      {       inhalt = await sr.ReadToEndAsync(); // Hier wird die Kontrolle an den Arufrufer gegeben       // das Lesen erfolgt in eigenem Thread      }      // Der Rest der Methode wird auch in eigenem Thread abgearbeitet      Console.WriteLine("ReadAsync(): Thread=" + System.Threading.Thread.CurrentThread.ManagedThreadId);      Console.WriteLine("ReadAsync(): Länge des Inhalts=" + inhalt.Length);      return inhalt;     } 
Schlusswort

Sicherlich hätten andere Kollegen eine andere Auswahl getroffen. In unseren Kundenprojekten habe ich es aber viel mit ASP.NET und Entity Framework zu tun, weshalb mir dies näher liegt als WPF. Und in WPF sind wirklich nur Kleinigkeiten beim Data Binding verbessert worden. Mit mir gehadert habe ich noch, ob ich das ASP.NET-Web-API in die Liste aufnehmen sollte. Aber ist es wirklich ein Highlight einer neuen .NET-Version, wenn Microsoft erst die Windows Communication Foundation (WCF) als jahrelang universelle Kommunikationsplattform präsentiert und dann jetzt für das Thema REST Web Services eine weitere Bibliothek schafft?

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -