Freigeben über


Innovationen

Master-Detail-Ansichten mit der ASP.NET Ajax-Bibliothek

Dino Esposito

Beispielcode herunterladen.

In den letzten Artikeln wurde eine Reihe datenbezogener Features der demnächst erscheinenden Betaversion der ASP.NET Ajax-Bibliothek besprochen, die jetzt Teil der CodePlex Foundation (CodePlex.org) ist. Es begann im letzten September mit einem Blick auf einige der neuen Features in Web Forms und ging mit einigen anderen Zwischenstationen auf dem Gebiet von ASP.NET AJAX weiter. Namentlich wurden die Themen Clientseitige Vorlagen und Datenbindung, Bedingtes Rendering und Livebindung angeschnitten

Wenn von datengesteuerten Webseiten die Rede ist, ist meistens wirklich eine Master-Detail-Ansicht einiger übergreifend verwandter Daten gemeint. Master-Detail-Ansichten eignen sich optimal zum Rendering von 1:n-Beziehungen. Da diese Beziehungen in der Praxis so häufig vorkommen, ist eine Webplattform, die keine effizienten Werkzeuge für diese Funktionalität bietet, inadäquat.

ASP.NET Web Forms bieten seit jeher eine starke Unterstützung für die Datenbindung und leistungsstarke Datenquellen- und datengebundene Serversteuerelemente. In Web Forms lassen sich Datenhierarchien sehr gut mit Serversteuerelementen darstellen, wobei fast jede mögliche Kombination von Rastern, Listen und Dropdownfeldern verwendet wird und mehrere Verschachtelungsebenen unterstützt werden.

Der Nachteil der Ansichten, die von Web Forms-Serversteuerelementen erzeugt werden, besteht nicht in der Effizienz des Rendering, sondern im statischen Zustand.

Die Benutzer, die in einer Master-Detail-Ansicht navigieren, sehen sich in der Regel verschiedene Masterdatensätze an und führen einen Drilldown zu den Detaildaten der Datensätze aus, die sie interessieren. Diese Interaktion ist das Wesentliche der Master-Detail-Ansicht.

In einem klassischen Web Forms-Szenario kann jeder Drilldownvorgang einen Postback auslösen. Viele Postbacks – und das nachfolgende erneute Laden der Seiten – sind nicht unbedingt das, worüber sich die Benutzer heutzutage freuen.

Es gibt eine Alternative, aber auch die ist nicht ganz unproblematisch. Sie besteht im Grunde genommen darin, alle möglichen Daten, die sich der Benutzer möglicherweise ansehen möchte, vorab zu laden. Die Daten werden dann mit der Standardseite heruntergeladen und mithilfe von CSS-Styles ausgeblendet. Gleichzeitig werden alle Behandlungsroutinen für Benutzeraktionen neu geschrieben, sodass sie den ausgeblendeten Inhalt enthüllen statt einen Postback auszulösen. Wie Sie sehen, ist das kein einfacher Weg.

Die ASP.NET Ajax Library bietet in Verbindung mit jQuery viel mächtigere Tools und ermöglicht es, elegante und effiziente Master-Detail-Ansichten zu schreiben, die asynchrone Postbackvorgänge durchführen und dies auch nur dann, wenn es unbedingt erforderlich ist.

Die heimliche Macht des DataView-Steuerelements

Das DataView-Clientsteuerelement ist das grundlegende Werkzeug zum Erstellen von Master-Detail-Ansichten in der ASP.NET Ajax-Bibliothek. In Kombination mit der sys-attach-Funktion der ASP.NET Ajax –Bibliothek und der Livebindung, stellt das Steuerelement ein unvergleichliches Maß an Flexibilität bezüglich der Funktionalität und des Layouts zur Verfügung. Das DataView-Steuerelement kann nämlich sowohl zum Erzeugen von Masteransichten als auch zum Erzeugen von Detailansichten dienen.

Um eine Master-Detail-Ansicht mit der ASP.NET Ajax-Bibliothek einzurichten, müssen Sie drei grundlegende Schritte ausführen. Erstens erstellen Sie den Markup für die Masteransichten und Detailansichten. Zweitens fügen Sie jeder Ansicht eine Instanz des DataView-Steuerelements als Verhaltenskomponente hinzu. Drittens nutzen Sie die Livebindung (oder ganz einfache Datenbindungsausdrücke), um das visuelle Layout der Detailansicht mit frischen Daten zu füllen. Beachten Sie, dass Vorlagen, Bindung und Komponenten im Code sowohl deklarativ als auch imperativ erstellt werden können. Beginnen wir mit einem einfachen Beispiel, das Sie mit dem Ansatz und den verfügbaren Tools vertraut machen soll.

Erstellen einer einfachen Master-Detail-Ansicht

Es folgt ein einfaches Layout für die Master-Detail-Ansicht. Es besteht im Grunde genommen aus zwei DIV-Tags: Das DIV-Tag für die Masteransicht enthält eine nicht geordnete Liste von Elementen. Das DIV-Tag für die Detailansicht enthält dagegen eine untergeordnete Tabelle:

<div id="masterView">
  <ul class="sys-template">
  ...
  </ul>
</div>
<div id="detailView">
  <table>
    <tr>
      <td> ... </td>
      <td> ... </td>
    </tr>
    ... 
  </table>
</div>

Meistens werden die Daten, die auf einer AJAX-Seite angezeigt werden, mit einem Webdienst, einem WCF-Dienst (Windows Communication Foundation) und natürlich jedem Dienst, der JSON (JavaScript Object Notation) erzeugen kann, von einem Webserver abgerufen. Die Daten werden üblicherweise als JSON-Stream übertragen. Es steht Ihnen frei, das Senden der Anforderungen an die Datenquelle selbst zu verwalten und ein eigenes AJAX-Framework Ihrer Wahl zu verwenden, z. B. Microsoft AJAX, jQuery oder einfache XmlHttpRequest-Aufrufe.

Das DataView-Steuerelement bietet auch eine Art von All-inclusive-Service. Sie richten es auf einen externen Datenanbieter, z. B. einen Webdienst, geben den aufzurufenden Vorgang an und listen die Parameter auf. Alle abgerufenen Daten können automatisch überall dort auf der HTML-Seite angezeigt werden, wo das DataView-Steuerelement hinzugefügt wurde. Abbildung 1 enthält den Markupcode, der für die Masteransicht erforderlich ist.

Abbildung 1 Die Masteransicht

<div>
  <ul class="sys-template" sys:attach="dataview"
    id="masterView"
    dataview:autofetch="true"
    dataview:dataprovider="/ajax40/mydataservice.asmx"
    dataview:fetchoperation="LookupCustomers"
    dataview:fetchparameters="{{ {query: ‘A’} }}"
    dataview:selecteditemclass="selecteditem"             
    dataview:initialselectedindex="0">
    <li>
      <span sys:command="Select">
      <span>{binding CompanyName}</span>
      ,  
      <span>{binding Country}</span>
      </span>        
    </li>
  </ul>
</div>

Mit dem Attribut sys:attach wird dem UL-Tag eine neue Instanz des DataView-Steuerelements angefügt. Der Codeausschnitt zeigt es nicht, aber zum Anfügen ist notwendig, dass der Name "dataview" deklariert und dem JavaScript-Objekt, das das Verhalten repräsentiert, zugeordnet wird. In der Regel deklarieren Sie die JavaScript-Objekte, die Sie verwenden möchten, im BODY-Tag:

<body xmlns:sys="javascript:Sys" 
      xmlns:dataview="javascript:Sys.UI.DataView">
  ...
</body>

Eigenschaften, die Sie deklarativ für die automatisch erstellte Instanz des DataView-Steuerelements festlegen, definieren den Remoteaufruf, mit dem die Daten abgerufen werden. In diesem Beispiel wird die LookupCustomers-Methode für MyDataService.asmx aufgerufen, wobei ein Zeichenfolgenparameter mit dem Wert A übergeben wird.

Außerdem kann das DataView-Steuerelement einige Eigenschaften akzeptieren, die dem Master-Detail-Szenario eigen sind. Die selectedItemClass-Eigenschaft gibt den CSS-Style an, der für die Elemente in der Elementvorlage verwendet werden, die aktuell als ausgewählt markiert sind. Die initialSelectedIndex-Eigenschaft verweist auf das Element in der Ansicht, das als ausgewählt markiert werden muss, wenn das DataView-Steuerelement zum ersten Mal seine Daten rendert.

Der Textkörper des UL-Tag enthält die Elementvorlage, und die abgerufenen Daten werden über die Livebindungssyntax der ASP.NET Ajax-Bibliothek daran gebunden:

<span>{binding CompanyName}</span>

Hier könnte auch ein einfacher Datenbindungsausdruck verwendet werden:

<span>{{ CompanyName }}</span>

Ein einfacher Datenbindungsausdruck ist ausreichend, wenn schreibgeschützte Daten angezeigt werden sollen. Wenn der Code die angezeigten Daten ändert und die Änderungen in Echtzeit angezeigt werden sollen, sollten Sie stattdessen die Livebindung verwenden. In Abbildung 2 ist die Detailansicht dargestellt.

Abbildung 2 Die Detailansicht

<div class="sys-template" sys:attach="dataview"
     dataview:data="{binding selectedData, source=$masterView}">
  <table>
    <tr>
       <td><b>Contact</b></td>
       <td><input id="contact" type="text" 
                  sys:value="{{ContactName}}"/></td>
    </tr>
    <tr>
       <td><b>Address</b></td>
       <td><input id="address" type="text" 
                  sys:value="{binding Street}"/></td>
    </tr>
    <tr>
       <td><b>City</b></td>
       <td><input id="city" type="text" 
                  sys:value="{binding City}"/></td>
    </tr>
    <tr>
       <td><b>Phone</b></td>
       <td><input id="phone" type="text" 
                  sys:value="{binding Phone}"/></td>      
    </tr>
  </table>
</div>

Sie werden bemerkt haben, dass das value-Attribut mit Namespace angegeben wird. Ab der Betaversion der ASP.NET Ajax-Bibliothek müssen alle Attribute, die {{expression}} oder {binding ...} enthalten, mit dem Sys-Präfix angegeben werden, da sie sonst ignoriert werden.

Der interessanteste Teil des in Abbildung 2 dargestellten Codes ist der Ausdruck, der der data-Eigenschaft des DataView-Steuerelements zugewiesen wird:

{binding selectedData, source=$masterView}

Aus der Syntax geht hervor, dass die Werte für die in der Ansicht enthaltenen Elemente aus dem Objekt namens masterView stammen. Die Eigenschaft, die physisch Daten für die Detailansicht bereitstellt, heißt selectedData. Natürlich wird auch das Objekt namens masterView eine Eigenschaft namens selectedData besitzen, da andernfalls ein Fehler auftritt. Abbildung 3 zeigt die Beispielseite in Aktion.

Abbildung 3 Eine auf dem DataView-Steuerelement basierende Master-Detail-Ansicht

image: A Master-Detail View Based on the DataView Control

Mehr Kontrolle über den Abrufprozess

Im ersten Beispiel wurde die Masteransicht im DataView-Steuerelement so konfiguriert, dass sie den automatischen Datenabruf unterstützt. Das bedeutet, dass das DataView-Objekt den angegebenen Abrufvorgang unmittelbar nach seiner Initialisierung auslösen muss.

Für viele Anwendungen ist dies zweifellos eine gute Lösung, in manchen Szenarios ist jedoch mehr Kontrolle über den Abrufprozess erforderlich. Genauer gesagt, ist es häufig notwendig, dass der Datenabruf nach einer Benutzeraktion eingeleitet wird. Im nächsten Beispiel wird der obige Code überarbeitet und eine Schaltflächenleiste hinzugefügt, mit der Sie den Anfangsbuchstaben der Namen der Kunden auswählen können, die angezeigt werden sollen. In Abbildung 4 ist der fertige Bildschirm dargestellt.

Abbildung 4 Beginnen mit bedarfsgesteuerter Datenbindung

image: Starting Data Binding On-Demand

Es gibt viele verschiedene Möglichkeiten, dynamisch eine Reihe ähnlicher DOM-Elemente zu generieren. Sie können die DOM-API verwenden oder sich vielleicht auf die etwas abstraktere Programmierschnittstelle der Microsoft AJAX-Bibliothek verlegen. Oder Sie können sich für jQuery entscheiden. Es folgt ein Codeausschnitt, in dem die Dienste der jQuery-Bibliothek verwendet werden, um eine Schaltfläche für jeden möglichen Anfangsbuchstaben der Kundennamen zu generieren:

 

for(var i=0; i<26; i++) {
  var btn = $(‘<input type="button" 
    onclick="filterQuery(this)" />’);
  var text = String.fromCharCode(‘A’.charCodeAt(0) + i);
  btn.attr("value", text).appendTo("#menuBar").show();
}

Die Funktion $ gibt ein DOM-Element zurück, das sich aus der angegebenen Markup-Zeichenfolge resultiert. Dann wird die value-Eigenschaft auf einen Großbuchstaben festgelegt und das DOM-Objekt an einen Platzhalter weiter unten auf der Seite angehängt. Der Code wird in der pageLoad-Funktion ausgeführt.

Jede auf diese Weise hinzugefügte Eingabeschaltfläche wird an denselben onclick-Ereignishandler gebunden. Der onclick-Ereignishandler akzeptiert einen Verweis auf das DOM-Objekt und aktualisiert die Masteransicht. Es folgt der Quellcode für den onclick-Ereignishandler:

function filterQuery(button) {
  // Enables live binding on the internal object that contains 
  // the current filter
  Sys.Observer.setValue(currentQuery, "Selection", button.value);

  // Update the master view
  fillMasterView(currentQuery);
}

Beachten Sie, dass in diesem Beispiel nachverfolgt werden muss, welcher Filter aktuell zur Auswahl einer Teilmenge der Kunden angewendet wird. Um die mühsame Bindung umgehen und Raum für künftige Erweiterungen zu lassen, entschied ich mich für ein benutzerdefiniertes Objekt mit einer einzigen Eigenschaft. Das Objekt ist global auf der Seite gültig und wird wie folgt initialisiert:

var currentQuery = { Selection: "A" };

Die aktuelle Auswahl wird auch über eine datengebundene Beschriftung auf der Seite angezeigt. Beachten Sie, dass der Namespace im innerHTML-Attribut des SPAN-Tags angegeben wird:

<h3>
  Selected customers: 
  <span sys:innerhtml=
    "{binding Selection, source={{currentQuery}}}">
  </span>
</h3>

Wenn der Benutzer auf eine Schaltfläche klickt, um die Auswahl zu ändern, wird die Selection-Eigenschaft des Objekts aktualisiert. Beachten Sie, dass der hier dargestellte, stark vereinfachte Code in der Praxis nicht funktioniert:

currentQuery.Selection = button.value;

Sie dürfen wahrnehmbare Änderungen nur über die setValue-Methode eingeben. Dies wird in dem Codeausschnitt weiter oben gezeigt, in dem die Sys.Observer.setValue-Methode verwendet wird

Wie steht es um den Code, der die Masteransicht programmgesteuert mit Werten füllt?

Anfangs müssen Sie die Instanz des DataView-Steuerelements ermitteln, die hinter der Masteransicht im Einsatz ist. Wie bereits erwähnt, wird das sys-key-Attribut nur intern zur Datenbindung verwendet. Um eine Komponente wie DataView abzurufen, müssen Sie auf die registrierten Anwendungskomponenten zugreifen, die von der $find-Method der Microsoft AJAX-Bibliothek verfügbar gemacht wird. Die Auswahl erfolgt über die ID des Stammtags:

var dataViewInstance = Sys.get("$masterView");

In diesem Beispiel soll das masterDataView-Objekt die ID des UL-Tags bilden, das mit dem sys-template-Attribut markiert ist, das die Masteransicht rendert:

<div>
  <ul class="sys-template" 
    sys:attach="dataview" 
    ID="masterDataView"
    ...>
  ...
  </ul>
</div>

Nachdem die DataView-Instanz ermittelt wurde, die die Masteransicht mit Daten füllt, werden die Abrufparameter angepasst und die DataView-Instanz angewiesen, neuere Daten abzurufen. Der hierzu erforderliche Code lautet wie folgt:

function fillMasterView(query) {
  // Retrieves the DataView object being used
  var dataViewInstance = Sys.get("$masterDataView");

  // DataView fetches fresh data to reflect current selection 
  var filterString = query.Selection;
  dataViewInstance.set_fetchParameters({ query: filterString });
  dataViewInstance.fetchData();
}

Die fetchData-Methode für das DataView-Steuerelement setzt mithilfe der aktuell eingestellten Anbieter, Vorgang und Parameter einen Remoteaufruf ab und aktualisiert die Ansicht mit den heruntergeladenen Daten.

Hinzufügen von Zwischenspeicherfunktionen

Betrachten wir das tatsächliche Verhalten der Seite, das in Abbildung 4 dargestellt wird. Jedes Mal, wenn auf eine Schaltfläche geklickt wird, um eine Teilmenge an Kunden auszuwählen, wird eine Remoteanforderung (die auch asynchron ist) generiert. Für jede nachfolgende Auswahl zur Anzeige von Details zu einem bestimmten Kunden ist kein Roundtrip mehr erforderlich, solange die anzuzeigenden Daten für die Bindung verfügbar sind.

Das hängt vorwiegend davon ab, was der Abrufvorgang tatsächlich zurückgibt. In diesem Beispiel ist die LookupCustomers-Methode wie folgt konzipiert:

public IList<Customer> LookupCustomers(string query);

Die Eigenschaften der Customer-Klasse bilden eine Datenmenge, die bereits zur Bindung verfügbar ist, ohne weitere Aktionen zu erfordern. Wenn Sie beispielsweise die Liste der Bestellungen für jeden Kunden anzeigen möchten, können Sie dies tun, ohne eine zusätzliche Anforderung senden zu müssen, aber nur dann, wenn die Bestellungen der Customer-Klasse beigefügt und zusammen mit der ersten Anforderung übermittelt werden.

In einem künftigen Artikel werde ich Lazy-Loading-Szenarios angehen und diese mit dem AJAX-spezifischen Predictive Fetch-Mustern mischen. Hier wollen wir jedoch einfach annehmen, dass die Daten, die wir vom Abrufvorgang des DataView-Objekts erhalten, für die Erstellung einer effizienten Benutzeroberfläche ausreichend sind.

Wenn Sie Kunden, deren Namen mit "A" beginnen, zwei- oder mehrmals anfordern, sendet die bisherige Version der Lösung getrennte Anforderungen, um praktisch dieselbe Datenmenge abzurufen. Eine Möglichkeit, diesen Aspekt zu verbessern, besteht im Hinzufügen clientseitiger Zwischenspeicherungsfunktionen. Die jQuery-Bibliothek stellt hierfür über ihre Kernfunktionen praktischerweise einen ausgezeichneten Cache im Arbeitsspeicher bereit

Eine andere Alternative wäre, die Antwort vom Browser zwischenspeichern zu lassen. Dann sendet der Browser, obwohl Daten erneut mit XmlHttpRequest angefordert werden, in Wirklichkeit keine weitere Anforderung.

Die jQuery-Cache-API

Von außen betrachtet ist der jQuery-Cache nichts Anderes als ein anfangs leeres Array, das mit Schlüsseln und Werten gefüllt wird. Sie können mit der jQuery-Cache-API auf zwei Ebenen arbeiten: Die Low-Level-API wird durch die Cache-Array-Eigenschaft dargestellt; die höhere data-Funktion bietet etwas mehr Abstraktion und erspart es Ihnen, zu überprüfen, ob der Array leer ist.

Es folgt der Code, mit dem ein Eintrag im Cache erstellt und Daten im Cache gespeichert werden:

// Initializes the named cache if it doesn’t exist 
if (!jQuery.cache["YourNamedCache"])
   jQuery.cache["YourNamedCache"] = {};

// Stores a key/value pair into the named cache
jQuery.cache["YourNamedCache"][key] = value;

Genauer gesagt, ist der jQuery-Cache als Gruppe benannter Caches strukturiert, die unter dem globalen Cache-Array gruppiert werden. Um einen Wert aus dem Cache zu lesen, ist folgender Code erforderlich:

var cachedInfo;
if (jQuery.cache["YourNamedCache"])
  cachedInfo = jQuery.cache["YourNamedCache"][key];

Auf diese Weise können Sie die Struktur der Daten im Cache steuern. Jeder benannte Cache wird nur bei Bedarf erstellt, und es können keine Daten dupliziert werden.

Die data-Methode bietet eine etwas reichhaltigere Programmierschnittstelle, die einige vorläufige Tests hinsichtlich des Vorhandenseins eines gegebenen benannten Cache kapselt. Überdies ermöglicht die data-Methode es Ihnen, den benannten Cache an ein oder mehrere DOM-Elemente anzufügen. Die data-Methode stellt eine grundlegende get/put-Schnittstelle zur Verfügung, mit der Sie Daten in den Cache schreiben und daraus lesen können.

Es folgt ein Beispielbefehl, mit dem ein gegebener Wert einem Schlüssel zugewiesen wird, der in dem einem gegebenen DOM-Element zugeordneten Cache erstellt wird:

$('#elemID').data(key, value);

Der benannte Cache wird bei Bedarf erstellt, wenn der Code versucht, auf den Inhalt zuzugreifen oder den Inhalt zu ändern. Die Bibliotheksfunktion erstellt einen benannten Cache für das angegebene Element und legt einen Namen fest. Der Name ist eine fortlaufende Nummer, die im expando-Attribut des DOM-Elements gespeichert wird.

Die data-Methode arbeitet mit dem Inhalt einer umschlossenen Menge. Wenn Sie eine umschlossene Menge definieren, die mehrere Knoten enthält, dann wird jedem Element ein eigener benannter Cache zugeordnet, der dieselben Daten enthält. Allerdings werden keine Daten dupliziert, weil in verschiedenen Cacheelementen auf gleiche Inhalte verwiesen und der Inhalt nicht geclont wird.

Betrachten wir folgendes Beispiel:

$('div').data('A', fetchedData);

In diesem Code wird versucht, einen Eintrag mit dem Schlüsselwert "A" in dem benannten Cache für die einzelnen auf der Seite vorhandenen DIV-Tags zu speichern. Auf diese Weise können Daten aus jedem DIV-Tag einer Seite abgerufen oder dafür festgelegt werden. Beispielsweise werden mit den beiden folgenden Codezeilen dieselben Daten abgerufen:

// Data is actually stored in one place but referenced from many
var data1 = $('div').data('A');

// If the ID references a DIV in the same page, 
// the returned data is the same as with the previous code
var data2 = $('#ThisElementIsDiv').data('A');

Die jQuery-Cache-API wird üblicherweise verwendet, um Daten in dem DOM-Element zu speichern, in dem diese Daten tatsächlich verwendet werden. Im vorliegenden Beispiel bestünde die anerkannte Lösung darin, Kunden in dem DIV-Element zwischenzuspeichern, das an die Masteransicht gebunden ist.

Zusammenfassung

Abbildung 5 zeigt die endgültige Version des Beispielcodes, mit dem Daten von einem Remotedienst abgerufen, mithilfe von jQuery lokal zwischengespeichert und in einem DataView-Steuerelement angezeigt werden.

Abbildung 5 Zwischenspeichern abgerufener Daten

var currentQuery = { Selection: "A" };

function pageLoad() {
  // Build the button bar to select customers by initial
  for(var i=0; i<26; i++) {
    var btn = $('<input type="button" 
         onclick="filterQuery(this)" />');
    var text = String.fromCharCode('A'.charCodeAt(0) + i);
    btn.attr("value", text).appendTo("#menuBar").show();
  }

  // Refresh the list of customers
  fillMasterView(currentQuery);
}
function filterQuery(button) {
   Sys.Observer.setValue(currentQuery, "Selection", button.value);

   // Updates the master view
   fillMasterView(currentQuery);
}
function fillMasterView(query) {
   // Check cache first: if not, go through the data provider
   if (!reloadFromCache(query))
       reloadFromSource(query);
}
function reloadFromCache(query) {
   // Using the query string as the cache key
   var filterString = query.Selection; 
        
   // Check the jQuery cache and update
   var cachedInfo = $('#viewOfCustomers').data(filterString);
   if (typeof (cachedInfo) !== 'undefined') {
       var dataViewInstance = Sys.get("$masterView");
       dataViewInstance.set_data(cachedInfo);
       // Template automatically refreshed 
       return true;
   }
   return false;       
}
function reloadFromSource(query) {
   // Set the query string for the provider
   var filterString = query.Selection;
        
   // Tell the DataView to fetch
   var dataViewInstance = Sys.get("$masterView");
   dataViewInstance.set_fetchParameters({ query: filterString });
   dataViewInstance.fetchData(
          cacheOnFetchCompletion, null, null, filterString);
}
function cacheOnFetchCompletion(fetchedData, filterString) {
    if (fetchedData !== null) {
       $('#viewOfCustomers').data(filterString, fetchedData);
    }
}

Überarbeitet wurde vor allem die Art und Weise, wie die Masteransicht mit Daten gefüllt wird. Zuerst wird überprüft, ob die Daten im lokalen Cache verfügbar sind. Wenn die Daten nicht gefunden werden, wird mit einer Remoteanforderung fortgefahren.

Die reloadFromCache-Methode gibt einen booleschen Wert zurück, der angibt, ob Daten erfolgreich aus dem Cache geladen wurden. Mit der set_data-Methode des DataView-Steuerelements wird das Steuerelement einer neuen Datenquelle zugewiesen. Als Nächstes wird die refresh-Methode aufgerufen, um die Ansicht zu aktualisieren.

Nachdem die Daten vom angegebenen Anbieter abgerufen wurden, werden sie im Cache gespeichert. Wichtig ist hier, dass die fetchData-Methode asynchron arbeitet. Das bedeutet, dass die get_data-Methode nicht unmittelbar nach der Rückkehr der fetchData-Methode aufgerufen werden kann:

dataViewInstance.set_fetchParameters({ query: filterString });
dataViewInstance.fetchData();
// At this point the method get_data can't retrieve yet 
// data for the new selection.

Die fetchData-Methode akzeptiert Befehlszeilenparameter. Sie müssen daher nur, wie unten gezeigt, einen Callback übergeben:

dataViewInstance.fetchData(
   cacheOnFetchCompletion,   // success callback
   null,                     // failure callback
   null,                     // merge option: append/overwrite
   filterString);            // context

Das erste Argument gibt den Callback für den Erfolgsfall an, der asynchron gestartet wird, nachdem der Datenabruf beendet wurde. Das zweite Argument enthält den Callback, der bei einem Fehlschlag aufgerufen werden soll. Das dritte Argument bezieht sich auf die merge-Option. Standardmäßig lautet diese Option AppendOnly, stattdessen kann auch OverwriteChanges angegeben werden. Beide Werte sind nur dann relevant, wenn das DataView-Steuerelement an ein AdoNetDataContext-Objekt gebunden wird. Das vierte Argument bezeichnet schließlich den Container für die Daten, die vom Callback für den Erfolgsfall zurückgegeben werden sollen.

Es folgt die Signatur der Callback-Funktion, die im Erfolgsfall aufgerufen wird:

function onSucceededFetch(fetchedData, context)

Im ersten Argument der Callback-Funktion werden die abgerufenen Daten gespeichert. Das zweite Argument enthält die Kontextinformationen, die über den Aufrufer angegeben wurden. In diesem Beispiel ist die Callback-Funktion cacheOnFetchCompletion der ideale Ort zum Zwischenspeichern abgerufener Daten. Der Kontextparameter enthält einfach die Abfragezeichenfolge für die zwischenzuspeichernden Daten.

Die Datenbindung ist eine feine Kunst und erfordert eine Menge Aufmerksamkeit und Ressourcen, um in einem AJAX-Szenario effizient sein zu können. Die ASP.NET Ajax-Bibliothek bietet eine Menge Tools zum Erstellen einer gültigen Datenbindungs- und Master-Detail-Lösung. Die Liste beinhaltet bemerkenswerte Auflistungen, Livebindungssyntax und das DataView-Steuerelelement. Die Betaversion der ASP.NET Ajax-Bibliothek kann unter ajax.codeplex.com heruntergeladen werden.

Dino Esposito ist Architekt bei IDesign und Mitverfasser des Buchs Microsoft .NET: Architecting Applications for the Enterprise (Microsoft Press, 2008). Esposito lebt in Italien und ist ein weltweit gefragter Referent bei Branchenveranstaltungen. Sie finden seinen Blog unter weblogs.asp.net/despos.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Dave Reed und Boris Rivers-Moore