Von der Datenbank bis zur Oberfläche mit .NET, Teil 3: Eine Weboberfläche mit ASP.NET

Hintergrundcode

Hintergrundcode der Buchungsmaske

Listing 3 zeigt den Hintergrund-Code. Bei jedem Seitenaufruf wird zu Beginn Page_Load() aufgerufen – eine gute Gelegenheit, den Webservice-Proxy zu instanziieren. Diese Instanz verwendet man im Ablauf der Seite, bis sie in Page_Unload() wieder vernichtet wird. Page_Load() lädt vom Webservice die Flughäfenliste und bindet sie an die beiden DropDownList-Steuerelemente. Diese Datenbindung muss nur beim erstmaligen Laden erfolgen, denn ASP.NET speichert die Daten in einem versteckten HTML-Feld mit Namen "Viewstate", daher steht dieser Code in der Bedingung !Page.IsPostBack. Viele Entwickler stören sich an dem zuweilen großen Viewstate, der zwischen Browser und Webserver immer wieder hin- und hergesendet wird. Die Alternative wäre aber hier sonst, bei jedem Roundtrip die Daten neu vom Webservice abzuholen. Auch das ist nicht effizient. Nachzudenken wäre allerdings darüber, diese wenig veränderlichen Daten auf dem Webserver im RAM zwischenzuspeichern.

Die Ereignisbehandlungsroutinen C_PassagierSuchen_Click() und C_FlugSuchen_Click() sind analog aufgebaut: Als Erstes prüft Int32.TryParse(), ob eine gültige Zahl eingegeben wurde. Wenn ja, wird die Suche über den Primärschlüssel mit GetFlug() und GetPassagier() gestartet. Im anderen Fall erfolgt die Suche über die Flugroute (GetFluege()) oder einen Namensbestandteil (GetPassagiere()).

Das GridView-Steuerelement erwartet als Datenquelle immer eine Menge. Übergibt man ein einzelnes Objekt, würde ASP.NET das mit dem Fehler "Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource." quittieren. Daher muss der Entwickler das Einzelobjekt in eine Liste verpacken, idealerweise in den gleichen Listentyp (ObservableCollection), den auch die Methoden GetFluege() und GetPassagiere() liefern. Die Datenbindung an den Inhalt des Attributs DataSource ist durch den Aufruf der Methode DataBind() auszulösen.

In C_Buchen_Click() gilt es zunächst zu prüfen, ob eine Auswahl in der Tabelle der Flüge und der Passagiere erfolgt ist. Zum Ermitteln des gewählten Flugs und des gewählten Passagiers greift der Programmcode auf SelectedDataKey[0] zurück. Dafür ist die Voraussetzung, dass in beiden <asp:GridView>-Tags im Attribut DataKeyNames festgelegt wurde, welches Geschäftsobjektattribut der Primärschlüssel enthält. Um zu verhindern, dass die Buchung ohne Auswahl startet, ist in Page_PreRender() hinterlegt, dass die "Buchen"-Schaltfläche nur aktiviert ist, wenn es eine Auswahl bei Flug und Passagier gibt.

Bei den beiden GridView-Steuerelementen lässt sich beobachten, dass eine einmal erfolgte Auswahl auch bei neuer Suche erhalten bleibt. Wenn der Anwender zum Beispiel bei einer Suche der Flüge nach "Rom" den zweiten auswählt und sich dann aber vor der Buchung doch entscheidet, nach "Paris" zu fliegen, ist nun der zweite Flug nach Paris ausgewählt. Das ist in dem Fall aber nicht sinnvoll, weswegen der Entwickler dieses Standardverhalten im GridView-Steuerelement durch den Zusatz EnablePersistedSelection="true" abstellen kann.

AJAX für die Buchungsmaske

Die bisherige Software erfüllt den gewünschten Zweck, aber beim Benutzer der Seiten gibt es leider einen hässlichen Effekt: Wenn er oben aus einer Liste einen Flug auswählt, dann nach unten scrollt, um einen Passagier zu wählen, ist er wieder oben auf der Seite und muss erneut scrollen. Das liegt daran, dass jede Aktion auf der Seite einschließlich der Auswahl eines Elements ein Neuladen der kompletten Seite durchführt. Die Seite wird jedes Mal komplett auf dem Server neu "gerendert".

Eine lupenreine Web-2.0-Anwendung würde jetzt bedeuten, dass man viel JavaScript programmiert, um per AJAX nur die Daten vom Webservice BuchungsService zu beziehen und dann per JavaScript die Oberfläche zu verändern. Das ist im Vergleich zur .NET-Programmierung aufwendig. ASP.NET bietet daher eine Kompromisslösung, eine Art "AJAX light". Dabei definiert der Entwickler mit einem <asp:UpdatePanel>-Element einen Bereich in einer ASPX-Seite, der sich einzeln aktualisieren lässt.

Zugehörige Trigger geben an, wann es zur Aktualisierung kommt. Der Server rendert dann nur die auszutauschenden Seitenfragmente, die per JavaScript (das bei ASP.NET mitgeliefert wird) in die Seiten eingesetzt werden. Im konkreten Fall bietet es sich an, ein UpdatePanel um die beiden GridView-Steuerelemente sowie den unteren Bereich mit der Buchen-Schaltfläche und das danebenstehende Label-Steuerelement zu setzen.

Listing 4 zeigt die veränderte Buchung.aspx mit den UpdatePanel-Steuerelementen, Triggern und dem obligatorischen ScriptManager zu Beginn. Wichtig ist, dass der Entwickler nicht vergisst, dass das SelectedIndexChanged-Ereignis der GridView-Steuerelemente Trigger für das C_Buchen_UpdatePanel sein müssen, denn durch die Auswahl in den Tabellen wird ja gegebenenfalls die Schaltfläche "Buchen" aktiviert. Alternativ zu den statischen Triggern kann man die Aktualisierung einzelner UpdatePanel-Steuerelemente in der Hintergrund-Code-Datei auslösen. Abbildung 3 zeigt die komplette Seite im Visual-Studio-Webdesigner einschließlich der Möglichkeit, sich die Trigger zusammenzuklicken.

Buchung.aspx im Visual-Studio-Webdesigner (Abb. 3)