03.01.2011, 16:15 Uhr

Die Neuerungen von ASP.NET 4.0 (Teil 3)

Während es in der letzten Folge um das Thema URL-Routing ging, steht in dieser Folge das mit ASP.NET 4.0 eingeführte Steuerelement QueryExtender auf dem Programm, mit dem sich generische Suchmasken realisieren lassen.
Eine Anforderung, die bei nahezu jeder geschäftlichen Web-Anwendung besteht, ist eine Suchmaske, die möglichst viele Suchkriterien vereinigt. Die Frage, die dabei stets aufs Neue beantwortet werden muss ist, welche Felder Teil der Suchmaske sein sollen. Eine Lösung kann eine generische Suchmaske sein, die alle in Frage kommenden Felder zusammenfasst, die per logische Verknüpfungen kombiniert werden. Eine solche generische Suchmaske konnte bereits mit früheren ASP.NET-Versionen umgesetzt werden, mit dem QueryExtender-Control von ASP.NET 4.0 wird es aber deutlich einfacher. Bis .NET 3.5 musste die Datenquelle explizite Abfragen enthalten, um gefilterte Ergebnislisten abrufen zu können. Kommt eine SQL-basierte Datenquelle zum Einsatz, wird die notwendige Where-Bedingung dynamisch zur Laufzeit zusammengesetzt. Dieses Vorgehen ist aber aus mehrfacher Hinsicht nicht optimal. Zum einen entspricht dies nicht einer guten Softwarearchitektur und zum anderen eröffnet sie unter Umständen einen Angriffspunkt für „SQL Injection“. Auch sind die möglichen Filterbedingungen innerhalb eines SQL-Statements begrenzt. Verbesserungen durch Datenquellen-Controls Eine erste Verbesserung wurde mit den verschiedenen Datasource-Controls (LinqDataSource, EntityDataSource , ObjectDataSource, SqlDataSource und AccessDataSource) unter .NET 2.0 erzielt. Über Parameter ermöglichen sie eine Sortierung und Filterung zur Laufzeit. Das Beispiel in Abb. 1 verdeutlicht den Einsatz des SQLDataSource-Controls. Innerhalb eines GridView werden Produktdatensätze angezeigt, über das Eingabefeld txtFilter kann nach Produktnamen gefiltert werden. Das zuständige SQLDataSource-Contol erledigt dies über den definierten Filterparameter sowie die festgelegte FilterExpression.   Neu bei ASP.NET 4.0: Das QueryExtender-Control Das mit ASP.NET 4.0 eingeführte QueryExtender-Control ermöglicht die flexible Filterung einer Datenquelle zur Laufzeit. Die Datenquelle muss dazu nicht angepasst werden. Dafür können aber nur die beiden Datenquellen LinqDataSource und EntityDataSource auf diese Weise gefiltert werden, weitere Datenquellen werden aktuell nicht unterstützt. Beide Datenquellen implementieren IQueryableDataSource und ermöglichen somit das dynamische Erzeugen eines ExpressionTrees, der später in SQL umgewandelt und auf dem Datenbankserver ausgeführt wird. Das bedeutet, alle Filter- und Sortierbedingungen werden nicht clientseitig, sondern serverseitig ausgeführt. Das Listing in Abb. 2 zeigt ein einfaches Anwendungsbeispiel, bei dem als Datenquelle eine Datenquelle vom EntityDataSource verwendet wird. Das zu Grunde liegende Entity Framework-Modell umfasst die Tabellen Products, Customers und Orders aus der Northwind-Datenbank. Wie im ersten Beispiel zeigt ein DataGrid eine Liste der Produkte an. Über eine Textbox kann gezielt nach Produkten gesucht werden. Über das Attribut TargetControlID  wird der QueryExtender an die Datenquelle gebunden. Innerhalb eines QueryExtender Elements wird der gewünschte Filter über verschiedene Expression-Ausdrücke definiert. Derzeit existieren insgesamt 10 dieser nnnExpression-Klassen, die sich alle direkt oder indirekt von der abstrakten Klasse DataSourceExpression ableiten. Direkt von dieser Klasse leiten die folgenden fünf Expression-Klassen ab: ControlFilterExpression, OrderByExpression, DynamicFilterExpression, DynamicRouteExpression und OfTypeExpression.´ ControlFilterExpression Diese Klasse ermöglicht die einfache Realisierung einer Parent-Child-Filterung innerhalb dynamischer Daten Webseiten (ASP.NET Dynamic Data Web). Dazu werden zwei datengebundene Steuerelemente auf einer Seite kombiniert. Ein Steuerelement repräsentiert die Kopfdaten, das zweite die zugehörigen Detailangaben (Abb. 3). Das erste GridView zeigt alle Kunden an, nach der Selektion eines Kunden aus der Liste werden die zugehörigen Bestellungen des Kunden in der zweiten Liste angezeigt. ControlFilterExpression Abb. 4 zeigt die vollständige Implementierung des Beispiels. Als Datenquelle wird hier ein Linq to SQL Datenmodell verwendet, das die Northwind Tabellen Customers und Orders umfasst. Das QueryExtender Control filtert die zugehörigen Bestellungen eines ausgewählten Kunden aus der Datenquelle lqOrders. Damit dies funktioniert, wird dem QueryExtender als Zieldatenquelle lqOrder zugewiesen. Die ControlFilterExpression spezifiziert über das Attribut ControlID das übergeordnete (Master) Steuerelement und das Attribut Name enthält die Bezeichnung des Fremdschlüssels aus der untergeordneten Tabelle.  OrderByExpression Diese Klasse legt die Datenfelder sowie die Sortierreihenfolge (auf- bzw. absteigend) fest, wonach sortiert werden soll. Über asp.ThenBy Konstrukte werden mehrere Felder für die Sortierung festgelegt. DynamicFilterExpression und DynamicRouteExpression Die beiden Ausdrücke DynamicFilterExpression und DynamicRouteExpression kommen bei der Verwendung dynamischer Daten Webseiten (ASP.NET Dynamic Data Web) zum Einsatz. Als Beispiel wird hier die Verwendung der DynamicFilterExpression gezeigt. Auf einer Webseite werden die Artikel aus der Northwind Datenbank angezeigt. Über eine Auswahlbox soll eine Filterung nach Auslaufartikel (Discontinued Products) möglich sein. Abb. 5 zeigt die vollständige Webseite. Um die Funktionalität effizient umzusetzen, wird ein DynamicFilter Control mit dem QueryExtender verknüpft (Abb. 6). Da es sich bei der Spalte Discontinued der Products-Tabelle um ein Bit-Datenfeld (es wird auf System.Boolean gemappt) handelt, kommt das entsprechende Dynamic Data-Filtertemplate zum Einsatz und eine Auswahlbox wird automatisch erzeugt. OfTypeExpression Diese Klasse filtert alle Elemente aus, die nicht einem bestimmten Datentyp entsprechen. Die folgenden nnnExpression-Klassen leiten von der abstrakten Klasse  ParameterDatasourceExpression ab MethodExpression Reichen die vorhandenen Expression-Möglichkeiten nicht aus, kann über die MethodExpression ein eigener LINQ-Filterausdruck definiert werden. Der Filterausdruck wird innerhalb einer Methode festgelegt. Dabei handelt es sich um eine statische Methode in der zugehörigen Codebehind Datei. Der erste Parameter sowie der Rückgabetyp der Methode müssen vom Typ IQueryable oder IEnumerable sein. Innerhalb der Methode wird der gewünschte LINQ-Ausdruck definiert. Das Beispiel in Abb. 7 filtert alle Produkte heraus, die von einem festgelegten Lieferanten (Supplier) bezogen werden. CustomExpression Im Gegensatz zur der MethodExpression wird die CustomExpression ereignisorientiert aufgerufen. Da es sich bei der Ereignismethode nicht um eine static Methode handelt, kann innerhalb der Abfrage auf Steuerelemente der Seite Bezug genommen werden. Somit können Werte einzelner Controls für die Abfrage ausgewertet werden (Abb. 8). Bei der Ausführung der Abfrage wird die Methode FilterByPrice aufgerufen. Diese Methode wertet die Inhalte des Steuerelements txtMaxPrice aus und liefert nur Produkte zurück, die kleiner als der angegebene max. Preis sind. PropertyExpression Mithilfe der PropertyExpression können Eigenschaftswerte ausgewertet und gefiltert werden. Das Beispiel in Abb. 9 nutzt die PropertyExpression, um alle Produkte einer bestimmten Produktkategorie anzuzeigen. Die Angabe der ID der Produktkategorie erfolgt über den QueryString-Parameter Category. RangeExpression Diese Klasse erlaubt die Datenfilterung durch Angabe eines gültigen Wertebereichs. Das Beispiel in Abb. 10 zeigt die Verwendung dieses Suchausdrucks. Der QueryExtender schränkt die Datenquelle ein und filtert nach Produkten, die einen Preis in einem definierten Bereich besitzen. Die Werte für den max. und min. Preis werden über die QueryString-Parameter min und max festgelegt. Die Attribute MinType und MaxType legen die Grenzen des Bereichs fest. Mögliche Werte sind: Exclusive, Inclusive und None. SearchExpression Diese Klasse ermöglicht das Filtern nach Zeichenketten. Wie bei String.Equals kann über die Eigenschaft StringComparisonType auf den Vergleichsalgorithmus Einfluss genommen werden. Die Eigenschaft SearchType entscheidet darüber, welche Zeichenketten gefunden werden. Mögliche Werte sind StartsWith, Contains und EndsWith. In dem einleitenden Beispiel (Abb. 3) wurde eine SearchExpression verwendet, um eine Filterung nach Produktnamen zu realisieren. Zugriff auf einzelne Werte Wie in den gezeigten Beispielen deutlich wurde, benötigen die einzelnen Expression-Varianten mindestens zwei Angaben: 1. Auf welches Feld aus der Datenquelle bezieht sich die Abfrage. 2. Der konkrete Wert für die Abfrage. Interessant ist die Antwort auf die Frage, auf welche Werte zugegriffen werden kann, um den zweiten Punkt zu realisieren. In den Beispielen ist bereits gezeigt worden, wie mittels asp:ConrolParameter auf Werte bestimmter Controls zugegriffen werden kann. Wie in den Beispielen aus Abb. 9 und 10 deutlich wurde, können auch QueryString-Parameter dynamisch ausgewertet werden. Darüber hinaus stehen noch folgende Möglichkeiten bereit: CookieParameter, DynamicControlParameter, DynamicQueryStringParameter, FormParameter , Parameter, ProfileParameter, RouteParameter und SessionParameter. Mehr Flexibilität dank QueryExtender Die Verwendung des QueryExtender-Controls erlaubt eine dynamische und flexiblere Umsetzung von Abfragen zur Laufzeit. Die definierten Abfragen werden serverseitig ausgeführt und belasten somit nicht den Webserver. Damit lassen sich generische Suchen auf einfache Art und Weise umsetzen. Eine Einschränkung darf nicht vergessen werden: Es kommen nur die Datenquellen LinqDataSource und EntityDataSource in Frage. Marc André Zhou arbeitet als Entwickler in Frankfurt a.M.Peter Monadjemi


Das könnte Sie auch interessieren