Optimieren benutzerdefinierter Webparts für das WAN

Zusammenfassung: Machen Sie sich mit Techniken zum Minimieren der Auswirkungen auf die Bandbreite in benutzerdefinierten Webparts vertraut, einschließlich allgemeiner Vorschläge zu Formatvorlagen und spezieller Informationen und Codebeispiele zum Abrufen und Rendern von Daten aus SharePoint-Listen mithilfe alternativer Methoden. (24 gedruckte Seiten)

Steve Peschka Microsoft Corporation

Januar 2008

Betrifft: Microsoft Office SharePoint Server 2007, Windows SharePoint Services 3.0, ASP.NET AJAX 1.0

Inhalt

  • Einführung in die Optimierung benutzerdefinierter Webparts für das WAN

  • Wiederverwenden integrierter Formatvorlagen oder Erstellen benutzerdefinierter Formatvorlagen

  • Speichern des Status

  • Maximieren der Leistung von Webparts zur Anzeige von Daten

  • Schlussfolgerung

  • Zusätzliche Ressourcen

Einführung in die Optimierung benutzerdefinierter Webparts für das WAN

Bei der Entwicklung benutzerdefinierter Webparts für Websites mit einer hohen Wartezeit oder einer niedrigen Bandbreite müssen die gleichen allgemeinen Entwurfsprinzipien beachtet werden, die auch beim Erstellen von Seiten für diesen Umgebungstyp angewendet werden. Sie sollten sich darauf konzentrieren, Webparts zu entwerfen, mit denen Roundtrips zum Server sowie die über das Netzwerk gesendete Datenmenge minimiert werden. In diesem Artikel werden mehrere Techniken behandelt, mit denen Sie diese Entwurfsziele erreichen können.

Wiederverwenden integrierter Formatvorlagen oder Erstellen benutzerdefinierter Formatvorlagen

Wenn Ihr Webpart HTML ausgibt, verwenden Sie die integrierten Formatvorlagenklassen des Microsoft Office SharePoint Server 2007- und Windows SharePoint Services 3.0-Stylesheets Core.css. (In diesem Artikel werden Office SharePoint Server und Windows SharePoint Services zusammenfassend als Microsoft SharePoint-Produkte und -Technologien bezeichnet). Durch die Wiederverwendung dieser Formatvorlagen können Sie die Auswirkungen auf die Seite minimieren, da für die Seite kein zusätzliches Stylesheet nur zur Unterstützung Ihres Webparts heruntergeladen werden muss. Darüber hinaus hat der Benutzer nach dem ersten Websitebesuch bereits die Datei core.css in seinen Cache heruntergeladen. Mithilfe der Formatvorlagen von core.css stellen Sie sicher, dass zur Unterstützung von Formatvorlagen keine zusätzlichen Downloads erforderlich sind.

Wenn Sie benutzerdefinierte Formatvorlagen für Ihr Webpart benötigen, sollten Sie möglicherweise ein benutzerdefiniertes Stylesheet verwenden, das mit dem Blob-Cache verwendet werden kann. Wenn Sie das Stylesheet in einer Dokumentbibliothek speichern, kann ihm eine Cachefähigkeitsdirektive zugeordnet werden, damit es nach dem ersten Seitenzugriff nicht heruntergeladen werden muss. Dies hat geringere Auswirkungen auf die Website als vergleichsweise Inlineformate, die jeweils beim Rendern des Webparts über das Netzwerk übertragen würden.

Speichern des Status

Webparts müssen möglicherweise Informationen wie den Benutzer, die Anforderung und die Datenquelle nachverfolgen. Es gibt verschiedene Optionen zum Speichern des Status, deren Beschreibung jedoch den Rahmen dieses Artikels sprengen würden. Im Allgemeinen gibt es jedoch zwei häufig verwendete Optionen, die für Webparts verwendet werden können, nämlich ViewState und Cache des Servers. Vermeiden Sie nach Möglichkeit ViewState in einer Umgebung mit geringer Bandbreite oder hohen Wartezeiten, da damit der Seite Inhalte beim Download und als Postbacks hinzugefügt werden. Dies gilt für andere Statustypen, bei denen auch Daten über das Netzwerk übertragen werden, wie beispielsweise Abfragezeichenfolgen, ausgeblendete Felder und Cookies.

Mithilfe der Cache-Klasse des Servers können Sie Statusinformationen auf Serverebene speichern. Die Cache-Klasse des Servers hat den Nachteil, dass dies nicht wirklich für die Verwendung als benutzerbezogener Statusmechanismus vorgesehen ist (kann jedoch in Abhängigkeit von der jeweiligen Situation dafür angepasst werden). Darüber hinaus werden die Cacheinformationen nicht an alle Front-End-Webserver in der Serverfarm repliziert. Cache ist nicht geeignet, wenn Ihr Webpart diese Statusinformationen unabhängig davon benötigt, auf welchen Front-End-Webserver für eine Benutzeranforderung letztlich zugegriffen wird.

In diesem Fall können Sie auch Sitzungsstatus verwenden. Sitzungsstatus ist standardmäßig deaktiviert, wird jedoch aktiviert, wenn Sie Microsoft Office InfoPath Forms Services (IPFS) in einer Serverfarm aktivieren. In aktiviertem Zustand wird mithilfe von Microsoft SQL Server der Status nachverfolgt. Dies bedeutet, dass Sitzungsstatuswerte unabhängig davon verwendet werden können, welcher Front-End-Webserver die HTTP-Anforderung empfängt. Der Sitzungsstatus hat den Nachteil, dass die Daten im Arbeitsspeicher verbleiben, bis sie entfernt werden oder ablaufen. Im Sitzungsstatus gespeicherte große Datasets können deshalb bei unsachgemäßer Verwaltung die Serverleistung beeinträchtigen. Aufgrund dieser Einschränkungen raten wir von der Verwendung des Sitzungsstatus ab, außer dies ist unbedingt erforderlich.

Sie können auch experimentieren und ViewState für alle Seiten der Website auf off festlegen, indem Sie die Datei web.config für eine Webanwendung bearbeiten. Ein pages-Element mit einem enableViewState-Attribut ist enthalten. Wenn Sie echte Bedenken wegen der Größe von ViewState auf der Seite haben, können Sie dieses Attribut auf false festlegen (der Standardwert ist true). In diesem Fall müssen Sie Ihre Website und die gesamte Funktionalität gründlich testen, um sicherzustellen, dass sie funktionsfähig ist. Denn für einige Steuerelemente und Webparts muss möglicherweise ViewState auf on festgelegt werden.

Wenn Sie ein Webpart entwickeln und eine ähnliche Funktion wie ViewState verwenden müssen, aber nicht sicher sind, ob diese Funktion auf einer Seite verfügbar sein wird, können Sie stattdessen die ControlState-Methode verwenden, die neu in ASP.NET 2.0 ist. Sie müssten kurzum folgende Schritte ausführen:

  1. Registrieren Sie ControlState, indem Sie während der Initialisierung Page.RegisterRequiresControlState aufrufen.

  2. Setzen Sie die Methoden LoadControlState und SaveControlState außer Kraft.

  3. Verwalten Sie manuell Ihren Teil der Objektauflistung, die ControlState zugeordnet wird.

Maximieren der Leistung von Webparts zur Anzeige von Daten

Wenn Ihr Webpart zum Anzeigen von Daten verwendet wird, gibt es mehrere Möglichkeiten, um das Leistungsverhalten für die Endbenutzer zu optimieren. Im Allgemeinen müssen Sie ein Gleichgewicht zwischen der erforderlichen Anzahl von Roundtrips zum Server und der Datenmenge, die für eine Anforderung abgerufen werden soll, finden.

Festlegen von Rendergrenzwerten

Schließen Sie für Steuerelemente, die Datenzeilen ausgeben, eine Eigenschaft ein, mit der der Administrator die Anzahl der Zeilen steuern kann. In Abhängigkeit von der Wartezeit und der Bandbreite der Umgebung, in der das Steuerelement verwendet werden soll, kann die Datenmenge, die für jede Seitenanforderung wiedergegeben wird, gesteigert oder reduziert werden. Dies kann sich auch auf die Anzahl von Anforderungen auswirken, die zum Anzeigen aller Daten erforderlich sind.

Wenn die Anzahl der zurückgegeben Zeilen eine Eigenschaft ist, die von den Endbenutzern festgelegt werden kann, sollten Sie möglicherweise Einschränkungen hinzufügen, damit das Netzwerk durch die von einem Benutzer ausgewählten Optionen nicht überlastet wird.

Verwenden von Inline-XML-Dateninseln

Eine weitere Alternative zum Anzeigen von Daten ist die Verwendung einer Inline-XML-Dateninsel und das clientseitige Rendering. Hierbei werden alle angezeigten Daten auf der Seite als XML-Dateninsel ausgegeben. Clientseitiges Skript wird zum Rendern der Daten auf der Seite verwendet und ist verantwortlich für die Verwendung von XML-DOM zum Abrufen und Anzeigen der Daten aus der Dateninsel.

Dadurch können Sie alle Daten in einer einzigen Anforderung abrufen, damit die Anzahl der Roundtrips zum Server minimiert wird. Die Downloadgröße ist jedoch ganz offensichtlich größer, weshalb das erstmalige Laden der Seite länger dauert. Darüber hinaus muss bei Verwendung dieser Methode auf einer Seite, in der andere Steuerelemente verwendet werden, die Postbacks verursachen, diese große Datenmenge jedes Mal heruntergeladen werden. Diese Methode ist am besten geeignet für Situationen, in denen Sie wissen, dass dies nicht der Fall ist, oder verwenden Sie sie andernfalls als optionale Datenabrufmethode. Erstellen Sie beispielsweise eine öffentliche Eigenschaft, um nachzuverfolgen, ob alle Daten heruntergeladen werden sollten, damit ein Websiteadministrator das Verhalten steuern kann.

Es folgt ein relativ einfaches Beispiel für ein entsprechendes Webpart. Der gesamte Inhalt aus einer Liste wird gelesen und an eine Seite als XML ausgegeben, und die Daten werden mit clientseitigem ECMAScript (JScript, JavaScript) gerendert und durchsucht.

Hinweis

Der Code in diesem Artikel entspricht nicht unbedingt den bewährten Methoden zum Erstellen von Code von Microsoft. Der Code wurde für diesen Zweck vereinfacht. Für eine Produktionsumgebung müssten Teile des Codes geändert werden.

Das Webpart setzt die Render-Methode außer Kraft und ruft zunächst die Inhalte einer Liste in der Website Contacts ab.

// Get the current Web site.
SPWeb curWeb = SPContext.Current.Web;

// Get the list; LISTNAME is a constant defined as "Contacts".
SPList theList = curWeb.Lists[LISTNAME];

// Ensure the list was found.
if (theList != null)
{
…
}

Nachdem die Liste abgerufen wurde, wird mithilfe eines StringBuilder-Objekts die XML-Dateninsel erstellt, die an die Seite ausgegeben wird.

// stringbuilder to create the XML island
System.Text.StringBuilder sb = new System.Text.StringBuilder(4096);

// Create the island header.
sb.Append("<XML ID='spList'><Items>");

// Enumerate through each item.
SPListItemCollection theItems = theList.Items;

foreach (SPListItem oneItem in theItems)
{
   // Add the tag for an item.
   sb.Append("<Item ");

   // Put the attributes in a separate try block.
   try
   {
      sb.Append("Name='" + oneItem["First Name"] + "' ");
      sb.Append("Company='" + oneItem["Company"] + "' ");
      sb.Append("City='" + oneItem["City"] + "' ");
   }
   catch
   {
      // Ignore.
   }

   // Close out the item.
   sb.Append("/>");
}

// Close off the XML island.
sb.Append("</Items></XML>");

// Write out the XML island; writer is the 
// HtmlTextWriter parameter to the method.
writer.Write(sb.ToString());

Nachdem die XML-Daten der Seite hinzugefügt wurden, benötigen Sie noch ein Element zum Anzeigen der Daten. Hierfür wird der Seite ein einfaches <div>-Tag hinzugefügt.

// Add a <div> tag - this is where we'll output the data.
writer.Write("<div id='wpClientData'></div>");

Mit der nächsten Komponente der Schnittstelle werden die Daten durchsucht. In diesem Fall werden der Seite zwei Linkschaltflächen hinzugefügt. Die OnClientClick-Eigenschaft und die Attributes-Auflistung gilt es zu beachten. Für die OnClientClick-Eigenschaft ist festgelegt, dass eine benutzerdefinierte ECMAScript-Funktion (JScript, JavaScript) verwendet wird, mit der die Daten auf dem Client angezeigt werden.

Mithilfe der Attributes-Auflistung wird die Navigations-URL für LinkButton festgelegt. In diesem Fall soll LinkButton als Hyperlink gerendert werden, damit der Benutzer Feedback erhält, dass das Element klickbar ist, und wir entsprechende Maßnahmen ergreifen können, wenn darauf geklickt wird. In diesem Fall wird der #-Link als Navigations-URL verwendet, da wir nicht tatsächlich zu einem Ziel navigieren möchten. Es soll lediglich ein Link gerendert und erfasst werden, wenn darauf geklickt wird.

// Add the paging links.
LinkButton btn = new LinkButton();
btn.Text = "<< Prev";
btn.Attributes.Add("href", "#");
btn.OnClientClick = "renderPartData('PREV');";
btn.RenderControl(writer);

writer.Write("&nbsp;");

btn = new LinkButton();
btn.Text = "Next >>";
btn.Attributes.Add("href", "#");
btn.OnClientClick = "renderPartData('NEXT');";
btn.RenderControl(writer);

Anschließend fügen wir der Seite ausgeblendete Felder hinzu, um das Paging für das Steuerelement nachzuverfolgen. In diesem Fall soll die Seitengröße (wie viele Datensätze jeweils angezeigt werden sollen), die aktuelle Datensatznummer sowie die Gesamtanzahl der Datensätze nachverfolgt werden.

// Add fields to track the number of items to see in a page and 
//current item number.  PageSize is a web part property that can be set 
//to control the number of rows returned 
Page.ClientScript.RegisterHiddenField("pageSize", PageSize);
Page.ClientScript.RegisterHiddenField("curPage", "-4");
Page.ClientScript.RegisterHiddenField("totalItems",
   theItems.Count.ToString());

Wenn alle Elemente vorhanden sind, besteht der letzte Schritt der Render-Methode im Registrieren eines Startskripts, mit dem das JavaScript zum Rendern der Daten ausgeführt wird.

// Create a startup script to call our dataload method when the page 
//loads
this.Page.ClientScript.RegisterStartupScript(this.GetType(), JSCR_START,
   "renderPartData();", true);

Das JavaScript selbst ist in einer separaten Datei des Projekts mit dem Namen Scripts.js enthalten. Es ist als eingebettete Ressource konfiguriert und wird als Webressource an die Seite gesendet (z. B. webresource.axd). Der Code für den Download wird im OnPreRender-Ereignis des Webparts ausgeführt. Mit diesem Code wird zunächst durch Aufrufen der IsClientScriptIncludeRegistered-Methode sichergestellt, dass der Skriptverweis nicht bereits der Seite hinzugefügt wurde. Falls dies nicht der Fall ist, wird der Skriptverweis als include für die Seite registriert.

// Register our JScript resource.
if (!Page.ClientScript.IsClientScriptIncludeRegistered(JSCR_NAME))
   Page.ClientScript.RegisterClientScriptInclude(this.GetType(),
JSCR_NAME, Page.ClientScript.GetWebResourceUrl(this.GetType(),
"Microsoft.IW.Scripts.js"));

Für diese Methode müssen Sie außerdem die Webressource in der AssemblyInfo.cs-Klasse für das Projekt registrieren.

[assembly: WebResource("Microsoft.IW.Scripts.js", "text/javascript")]

Beim Rendern der Seite wird ein Link eingeschlossen, der so oder ähnlich aussieht.

<script src="/WebResource.axd?d=DVBLfJiBYH_yZDWAphRaGQ2&amp;t=633198061688768475" 
type="text/javascript"></script>

ECMAScript (JScript, JavaScript) liest die XML-Daten und rendert sie. Zunächst werden die Pagingbegrenzungen bestimmt, d. h., die Datensatznummer des ersten und letzten anzuzeigenden Datensatzes. Nachdem die anzuzeigenden Datensätze bekannt sind, wird mithilfe einer relativ einfachen Methode eine Instanz von MSXML auf dem Client erstellt.

function createXdoc()
{
   ets the most current version of MSXML on the client.
   var theVersions = ["Msxml2.DOMDocument.5.0", 
      "Msxml2.DOMDocument.4.0", 
      "MSXML2.DOMDocument.3.0", 
      "MSXML2.DOMDocument", 
      "Microsoft.XmlDom"];

   for (var i = 0; i < theVersions.length; i++)
   {
      try
      {
         var oDoc = new ActiveXObject(theVersions[i]);
         return oDoc;
      }
      catch (theError)
      {
         // Ignore.
      }
   }
}

Mithilfe von Variablen werden XML-Daten und ein Verweis auf das DIV-Element, in das die Ergebnisse ausgegeben werden, gespeichert.

// Get the XML.
var xData = document.getElementById("spList").innerHTML;
   
// Get the target.
var target = document.getElementById("wpClientData");

Anschließend werden die Daten von MSXML in das DOMDocument-Objekt geladen, und danach wird die Liste der Item-Elemente ausgewählt.

// Get the XML DomDocument.  
var xDoc = createXdoc();
   
// Validate that a document was created.
if (xDoc == null)
{
target.innerHTML = "<font color='red'>A required system component 
(MSXML) is not installed on your computer.</font>";
   return;
}
   
// Load the XML from the island into the document.
xDoc.async = false;
xDoc.loadXML(xData);
   
// Check for parsing errors.
if (xDoc.parseError.errorCode != 0) 
{
var xErr = xDoc.parseError;
target.innerHTML = "<font color='red'>The following error was 
encountered while loading the data for your selection: " + 
xErr.reason + "</font>";
   return;
}
   
// Get the items.  
var xNodes;
xDoc.setProperty("SelectionLanguage", "XPath");
xNodes = xDoc.documentElement.selectNodes("/Items/Item");  

Nachdem die Knoten ausgewählt wurden, können die Ergebnisse für den benötigten Seitensatz aufgelistet und auf der Seite gerendert werden.

// Check for data.
if (xNodes.length == 0)
   target.innerHTML = "<font color='red'>No data was found.</font>";
else
{
   // Create a table to render the data.
   output = "<table style='width:250px;'><tr><td>";
   output += "<table class='ms-toolbar'><tr>" +
      "<td style='width:75px;'>Name</td>";
   output += "<td style='width:75px;'>Company</td>";
   output += "<td style='width:75px;'>City</td></tr></table>
</td></tr>";
   output += "<tr><td><table>";
      
   // Cycle through all the data; startID and endID represent
   // the bounds of the page.
   for (var i = (parseInt(startID) - 1); i < parseInt(endID); i++)
   {
      // Create a new row.
      output += "<tr>";
                  
      // Add each cell.
      output += "<td style='width:75px;'>" + 
xNodes(i).getAttribute("Name") +
         "</td>";
      output += "<td style='width:75px;'>" +
         xNodes(i).getAttribute("Company") + "</td>";
      output += "<td style='width:75px;'>" + xNodes(i).getAttribute("City") +
         "</td>";
         
      // Close the row tag.
      output += "</tr>";
   }
      
   // Close the table tag.
   output += "</table>
</td></tr></table>";
      
   // Show the page parameters.
   output += "Records " + startID + " to " + endID + " of " + ti;
      
   // Plug the output into the document.
   target.innerHTML = output;
}

Nach Abschluss dieses Schritts zeigt das Webpart die Datensätze auf dem Client an und stellt eine Methode zum Navigieren bereit, ohne dass ein weiterer Roundtrip zum Server ausgeführt wird. In Abbildung 1 und Abbildung 2 ist das Webpart mit zwei unterschiedlichen Datenseiten dargestellt.

Abbildung 1. Webpart mit Daten

Erste Seite des Beispielwebparts

Abbildung 2. Webpart mit Daten

Zweite Seite von Webpartdaten

Verwenden von clientseitigem Skript, das eine Verbindung mit Webdiensten herstellt

Die Verwendung von clientseitigem Skript, das zum Abrufen von Daten eine Verbindung mit Webdiensten herstellt, ist eine weitere Möglichkeit zum Verwalten der über das Netzwerk gesendeten Daten. Bei dieser Methode können mit einem Steuerelement die erforderlichen Daten abgerufen werden, ohne dass ein Postback für die gesamte Seite erforderlich ist. Dies ist nicht nur für den Benutzer angenehmer, sondern reduziert auch die Auslastung des Netzwerks, da zum Abrufen der Daten nicht die gesamte Seite an den Server und wieder zurück gesendet wird. Das Abrufen der Daten ist möglich direkt mit der XmlHttp-Komponente oder mit Microsoft AJAX Library 1.0, womit XmlHttp-Funktionalität eingeschlossen ist.

XmlHttp und ASP.NET AJAX stellen zwei hochrangige Optionen dar. Wahrscheinlich ist es jedoch am einfachsten, ASP.NET AJAX in Verbindung mit einem benutzerdefinierten Webdienst zu verwenden, mit dem SharePoint-Listendaten verfügbar gemacht werden. Es ist zwar technisch möglich, die ASP.NET AJAX-Bibliotheken mit einem SOAP-basierten Dienst zu verwenden (wie z. B. die integrierten Webdienste der SharePoint-Produkte und -Technologien), aber dies ist wesentlich komplizierter und bietet nicht die zusätzlichen Vorteile von ASP.NET AJAX-Webdiensten, wie z. B. geringere Nutzlastgrößen und Unterstützung für JavaScript Object Notation (JSON).

SharePoint-Produkte und -Technologien bieten eine Reihe von Webdiensten, die Sie zum Anzeigen von Daten verwenden können. Mit dem Lists-Webdienst können Sie Tabellendaten aus Listen und Bibliotheken in SharePoint-Produkten und -Technologien abrufen. Hiermit können Sie die in Listen enthaltenen Daten und Links zu Elementen in einer Liste, wie z. B. Dokumente oder Bilder, rendern. Mit dem Search-Webdienst können Sie den Textkörper von Inhalten durchsuchen, der in SharePoint-Produkten und -Technologien und anderen externen Quellen, die gecrawlt werden, enthalten ist. Mit sorgfältig erstellten Abfragen auf Metadatenbasis können Sie mit diesem Dienst auch einen gefilterten Datensatz aus mindestens einer SharePoint-Liste abrufen.

Das folgende Beispiel veranschaulicht das Erstellen eines benutzerdefinierten Webdienstes zum Abrufen von Listendaten aus SharePoint-Produkten und -Technologien. Die Webdienstklasse wird mit dem System.Web.Script.Services.ScriptService-Attribut kommentiert. Auf diese Weise können Sie die clientseitigen Webdienstkomponenten verwenden, die mit ASP.NET AJAX bereitgestellt werden. Der Webdienst wird dann bei den SharePoint-Produkten und -Technologien "registriert", damit über das Verzeichnis _vti_bin mit allen integrierten Webdiensten darauf zugegriffen werden kann. Die Masterseite wird aktualisiert und um das ScriptManager-Steuerelement von ASP.NET AJAX und deklarative Tags für den benutzerdefinierten Webdienst ergänzt. Anschließend wird ein Webpart erstellt, mit dem das clientseitige Skript zum Abrufen von Daten über die ASP.NET AJAX-Komponenten vom benutzerdefinierten Webdienst und zum Anzeigen auf der Seite generiert wird.

Installieren von AJAX

Zunächst müssen Sie die Binärdateien für ASP.NET AJAX 1.0 auf Ihren SharePoint-Servern installieren. Sie können diese von der ASP.NET AJAX-Website (in englischer Sprache) herunterladen. Diese Binärdateien müssen Sie auf jedem Front-End-Webserver in der Serverfarm installieren.

Nach der Installation von ASP.NET AJAX müssen Sie die Datei web.config für jede Webanwendung aktualisieren, in der ASP.NET AJAX verwendet werden soll. Dies dauert etwas länger. Ausführliche schrittweise Anleitungen finden Sie im Blogbeitrag von Mike Ammerlan zum Thema Integrieren von ASP.NET AJAX in SharePoint (in englischer Sprache).

Erstellen eines benutzerdefinierten Webdienstes

Das Erstellen eines benutzerdefinierten Webdienstes zum Abrufen der Daten ist erforderlich, da Sie damit System.Web.Script.Services.ScriptService auf Ihre Webdienstklasse anwenden und demzufolge mit dem ASP.NET AJAX-Webdienstframework verwenden können. In diesem Fall wurde eine relativ einfache Webdienstklasse entwickelt, um Listendaten basierend auf grundlegenden Parametern abzurufen.

Die Webdienstklasse enthält einen Verweis auf die System.Web.Extensions-Klasse, um die Unterstützung von ASP.NET AJAX zu aktivieren. Nach dem Hinzufügen dieses Verweises wird der Klasse eine using-Anweisung (Microsoft Visual C#) oder Imports-Anweisung (Microsoft Visual Basic) hinzugefügt.

using System.Web.Script.Services;

Die Klasse wird dann mit dem ScriptService-Attribut ergänzt, damit es direkt vom AJAX ASP.NET-Webdienstframework verwendet werden kann. Es enthält einen Konstruktor ohne Standardparameter, sodass die Klasse durch ASP.NET AJAX serialisiert werden kann.

namespace Microsoft.IW
{
   [ScriptService]
   public class AjaxDataWebService : WebService
   {

      public AjaxDataWebService()
      {
         // Default constructor
   }

Im ersten Beispiel enthält der Webdienst nur eine Methode, die eine Zeichenfolge zurückgibt. Bei der Zeichenfolge handelt es sich um XML, das auf dem Client verwendet wird. Die Methodensignatur wird wie folgt definiert.

[WebMethod]
public string GetListData(string webUrl, string listName, int 
startingID, 
int pageSize, string[] fieldList, string direction)

// webUrl: URL to the Web site that contains the list
// listName: name of the list (such as "Contacts")
// startingID: used for paging data - which items to get
// pageSize: how many items to return
// fieldList: an array of fields to retrieve for each item
// direction: flag to indicate page forward or backward

StringBuilder ret = new StringBuilder(2048);
DataTable res = null;
string camlDir = string.Empty;
string camlSort = string.Empty;

Der erste Teil des Codes enthält einen Verweis auf die Website, das Web und die Liste, die die Daten enthält.

// Try getting the site.
using (SPSite theSite = new SPSite(webUrl))
{
   // Get the Web at the site URL.
   using (SPWeb theWeb = theSite.OpenWeb())
   {
      // Try getting the list.
      SPList theList = theWeb.Lists[listName];

Anschließend wird die Abfragesemantik basierend auf der Pagingrichtung, der Start-ID und der Größe des Resultsets erstellt.

// Use the direction to determine if we're going up or down.
// If we're going down, then sort it in descending order
// so that we go 20,19,18... for example, instead of 1,2,3; otherwise
// each time you paged backward you would always get the first
// page of records.
if (direction == "NEXT")
{
   camlDir = "<Gt>";
   camlSort = "TRUE";
}
else
{
   camlDir = "<Lt>";
   camlSort = "FALSE";
}

// Create the query where clause.
string where = "<Where>" + camlDir + "<FieldRef Name='ID'/>" +
   "<Value Type='Number'>" + startingID + "</Value>" +
   camlDir.Replace("<", "</") + "</Where>" +
   "<OrderBy><FieldRef Name='ID' Ascending='" + camlSort +
"'/></OrderBy>";

// Plug in the where clause.
qry.Query = where;

// Set the page size.
qry.RowLimit = (uint)pageSize;

// Create the view fields.
StringBuilder viewFields = new StringBuilder(1024);
foreach (string oneField in fieldList)
{
   // Add everything but the ID field; we’re doing the ID field 
   // differently because we need to include it for paging, 
   // but we can’t include it more than once because it would
   // result in the XML that is returned being invalid. So it
   // is special-cased here to make sure it is only added once.
   if (string.Compare(oneField, "id", true) != 0)
      viewFields.Append("<FieldRef Name='" + oneField + "'/>");
}

// Now plug in the ID.
viewFields.Append("<FieldRef Name='ID'/>");

// Set the fields to return.
qry.ViewFields = viewFields.ToString();

Nachdem die Abfrage somit konfiguriert wurde, muss sie im nächsten Schritt ausgeführt werden. Wenn die Ergebnisse zurückgegeben werden, wird erneut die Pagingrichtung überprüft. Beim Zurücknavigieren muss die Reihenfolge der Ergebnisse wieder umgekehrt werden, damit die Ergebnisse sortiert nach der kleinsten zur größten ID auf der Seite angezeigt werden. Dies dient ausschließlich zur Pagingunterstützung im Webpart. Zur Vereinfachung der Reihenfolge werden die Daten in ein DataTable-Objekt von ADO.NET abgerufen. Nachdem die Daten abgerufen und entsprechend sortiert wurden, wird jede Zeile aufgelistet, um den XML-Code zu erstellen, der vom Methodenaufruf zurückgegeben wird.

// Execute the query.
res = theList.GetItems(qry).GetDataTable();

// If we are going backward, we need to reorder the items so that
// the next and previous buttons work as expected; this puts it back 
// in 18,19,20 order so that the next or previous are based on 18
// (in this example) rather than 20.
if (direction == "PREV")
   res.DefaultView.Sort = "ID ASC";

// Create the root of the data.
ret.Append("<Items Count='" + theList.ItemCount + "'>");

// Enumerate results.
foreach (DataRowView dr in res.DefaultView)
{
   // Add the open tag.
   ret.Append("<Item ");

   // Add the ID.
   ret.Append(" ID='" + dr["ID"].ToString() + "' ");

   // Add each attribute.
   foreach (string oneField in fieldList)
   {
      // Add everything but the ID field.
      if (string.Compare(oneField, "id", true) != 0)
         ret.Append(oneField + "='" + dr[oneField].ToString() +
"' ");
   }

   // Add the closing tag for the item.
   ret.Append("/>");
}

// Add the closing tag.
ret.Append("</Items>");

Der gesamte vorausgehende Code befindet sich natürlich in einem try…catch-Block. Im finally-Block werden die dem DataTable-Objekt zugeordneten Ressourcen freigegeben, und anschließend wird der erstellte XML-Code zurückgegeben.

finally
{
   // release the datatable resources
   if (res != null)
      res.Dispose();
   res = null;
}

return ret.ToString();

Registrieren des benutzerdefinierten Webdienstes

Das Verfügbarmachen des benutzerdefinierten Webdienstes für die Verwendung im ASP.NET AJAX-fähigen Webpart erfordert zwei Konfigurationsarten. Zum einen muss er so konfiguriert werden, dass SharePoint-Produkte und -Technologien den Webdienst kennen und im Kontext einer SharePoint-Anwendung aufrufen können. Dies beinhaltet mehrere Schritte, die auf einer hohen Ebene ausgeführt werden müssen:

  1. Erstellen Sie die CodeBehind-Klasse für den Webdienst in einer separaten Assembly, und registrieren Sie diese im globalen Assemblycache.

  2. Generieren und bearbeiten Sie eine statische Discovery-Datei und eine WSDL-Datei (Web Services Description Language).

  3. Stellen Sie die Webdienstdateien im Verzeichnis _vti_bin bereit.

Mehrere Schritte sind erforderlich, um alle vorherigen Aktionen auszuführen. Glücklicherweise ist bereits ein Artikel zu diesem Thema verfügbar. Ausführliche Informationen finden Sie unter Exemplarische Vorgehensweise: Erstellen eines benutzerdefinierten Webdienstes.

Nachdem Sie den Webdienst in das SharePoint-Verzeichnis _vti_bin integriert haben, müssen Sie die Masterseite ändern, um ein <ScriptManager>-Tag von ASP.NET AJAX hinzuzufügen. Innerhalb des <ScriptManager>-Tags definieren Sie einen Diensteinstiegspunkt für den verwendeten benutzerdefinierten Webdienst. In diesem Beispiel heißt der benutzerdefinierte Webdienst ListData.asmx. Es folgt das vollständige Tag, das der Masterseite hinzugefügt wird.

<asp:ScriptManager runat="server" ID="ScriptManager1">
   <Services>
      <asp:ServiceReference Path="_vti_bin/ListData.asmx" />
   </Services>
</asp:ScriptManager>

Nachdem der Webdienst so konfiguriert wurde, dass er vom SharePoint-Verzeichnis _vti_bin aus aufgerufen werden kann, und nachdem das <ScriptManager>-Tag der Masterseite mit einem Verweis auf den benutzerdefinierten Webdienst hinzugefügt wurde, können die Clientkomponenten des ASP.NET AJAX-Webdienstes nun damit kommunizieren.

Erstellen eines benutzerdefinierten Webparts mithilfe von XML-Daten

Nun benötigen wir ein benutzerdefiniertes Webpart, um das ASP.NET AJAX-Clientskript zum Abrufen von Daten aus dem benutzerdefinierten Webdienst zu generieren. Das Webpart selbst enthält fast keine serverseitige Logik. Der Großteil des Codes ist in einer ECMAScript-Datei (JScript, JavaScript) enthalten, die vom Webpart als Webressource (Datei WebResource.axd) hinzugefügt wird.

Zunächst muss der gesamte HTML-Code generiert werden, der auf der Seite für die Benutzeroberfläche erforderlich ist. Es gibt zwei grundlegende Methoden zum Generieren von HTML-Code aus einem Webpart. Die einfachere Methode ist das direkte Erstellen der Tags als Zeichenfolgen. Eine kompliziertere, aber dafür sicherere Methode ist die Verwendung der ASP.NET-Klassenbibliotheken. Bei relativ einfachem HTML-Code ist es normalerweise schneller und einfacher die Zeichenfolgen direkt einzugeben. Der in diesem Fall verwendete HTML-Code ist etwas komplizierter. Er enthält drei <div>-Haupttags für die Daten, Navigationssteuerelemente und die Schnittstelle "please wait". Das "please wait"<div>-Tag enthält zwei geschachtelte <div>-Tags, um das Bild und den Text ordnungsgemäß im Webpart zu positionieren. Basierend auf diesen komplizierteren HTML-Anforderungen wurden, wie im folgenden Code veranschaulicht, die ASP.NET-Klassenbibliotheken zum Generieren des HTML-Codes verwendet.

// Add all the UI that is used to render the data.
// <div id='dataDiv' style='display:inline;'></div>
writer.AddAttribute(HtmlTextWriterAttribute.Id, "dataDiv");
writer.AddAttribute(HtmlTextWriterAttribute.Style, "display:inline;");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.RenderEndTag();

// Add a <div> tag to hold the navigation buttons.
// <div id='navDiv' style='display:inline;'>
writer.AddAttribute(HtmlTextWriterAttribute.Id, "navDiv");
writer.AddAttribute(HtmlTextWriterAttribute.Style, "display:inline;");
writer.RenderBeginTag(HtmlTextWriterTag.Div);

// Add the paging links inside the navigation <div> tag.
LinkButton btn = new LinkButton();
btn.Text = "<< Prev";
btn.Attributes.Add("href", "#");
btn.OnClientClick = "GetAjaxData('PREV');";
btn.RenderControl(writer);

writer.Write("&nbsp;");

btn = new LinkButton();
btn.Text = "Next >>";
btn.Attributes.Add("href", "#");
btn.OnClientClick = "GetAjaxData('NEXT');";
btn.RenderControl(writer);

// Close out the navigation <div> tag.
writer.RenderEndTag();

// Write the "please wait" <div> tag.
// <div id='waitDiv' style='display:none;'>
writer.AddAttribute(HtmlTextWriterAttribute.Id, "waitDiv");
writer.AddAttribute(HtmlTextWriterAttribute.Style, "display:inline;");
writer.RenderBeginTag(HtmlTextWriterTag.Div);

// Write the <div> tag to hold the "please wait" image. 
// <div style='float:left;'>
writer.AddAttribute(HtmlTextWriterAttribute.Style, "float:left;");
writer.RenderBeginTag(HtmlTextWriterTag.Div);

// Write the animated GIF tag.
// <img src='_layouts/images/gears_an.gif' alt='Please wait...'/>
writer.AddAttribute(HtmlTextWriterAttribute.Src, 
   "_layouts/images/gears_an.gif");
writer.AddAttribute(HtmlTextWriterAttribute.Alt, "Please wait...");
writer.RenderBeginTag(HtmlTextWriterTag.Img);
writer.RenderEndTag();

// Close the <div> tag for the image.
writer.RenderEndTag();

// Write the <div> tag for the text that goes next to the image.
// <div style='float:left;margin-top:22px;margin-left:10px;'>
writer.AddAttribute(HtmlTextWriterAttribute.Style,
   "float:left;margin-top:22px;margin-left:10px;");
writer.RenderBeginTag(HtmlTextWriterTag.Div);

// Write a header tag.
writer.RenderBeginTag(HtmlTextWriterTag.H4);
            
// Write the text.
writer.Write("Please wait while your data is being retrieved.");

// Close the header tag.
writer.RenderEndTag();

// Close the <div> tag for the text.
writer.RenderEndTag();

// Close the <div> tag for all of the "please wait" UI.
writer.RenderEndTag();

Im nächsten Schritt fügt das Webpart mehrere ausgeblendete Felder hinzu, mit denen der Status beim Paging von Daten nachverfolgt wird.

// Add fields to keep track of number of items to see in a page and 
//current item number. PageSize is a web part property that can be set 
//to control the number of rows returned
Page.ClientScript.RegisterHiddenField("siteUrl", 
SPContext.Current.Web.Url);
Page.ClientScript.RegisterHiddenField("listName", "Contacts");
Page.ClientScript.RegisterHiddenField("pageSize", PageSize);
Page.ClientScript.RegisterHiddenField("totalItems", "-1");
Page.ClientScript.RegisterHiddenField("startID", "-1");
Page.ClientScript.RegisterHiddenField("endID", "-1");

Schließlich wird ein Startskript registriert, damit beim Laden der Seite das Webpart die erste Seite mit Daten abruft:

Erstellen Sie ein Startskript, um die Methode zum Laden von Daten beim Laden der Seite aufzurufen.

if (!Page.ClientScript.IsStartupScriptRegistered(JSCR_START)) 
Page.ClientScript.RegisterStartupScript(this.GetType(), 
JSCR_START, "GetAjaxData('NEXT');", true);

Das ECMAScript (JScript, JavaScript), das zum Abrufen der Daten verwendet wird, wird im OnPreRender-Ereignis registriert. Hier wurde auch der gleiche Prozess zum Hinzufügen des Skripts als eingebettete Ressource und zum Registrieren in der Datei AssemblyInfo.cs wie beim XML-Dateninsel-Webpart verwendet. Die Registrierung der ECMAScript-Datei sieht folgendermaßen aus.

// Register our JScript resource.
if (!Page.ClientScript.IsClientScriptIncludeRegistered(JSCR_NAME))
Page.ClientScript.RegisterClientScriptInclude(this.GetType(),
JSCR_NAME, Page.ClientScript.GetWebResourceUrl(this.GetType(),
"Microsoft.IW.ajaxdata.js"));

Mit dem erstellten HTML-Code kann die JScript- oder JavaScript-Datei eine Schnittstelle "please wait" generieren, wenn die Seite geladen wird und Daten abgerufen werden sowie jedes Mal, wenn eine neue Seite mit Daten abgerufen, weil der Benutzer auf die Verknüpfung Next oder Prev geklickt hat. In diesem Fall stammt das verwendete animierte GIF aus SharePoint-Produkten und -Technologien und ist deshalb den Benutzern bereits vertraut. Die Schnittstelle "please wait" sieht folgendermaßen aus.

Abbildung 3. Schnittstelle "please wait"

Meldung zum Abruf von Webpartdaten

Alle Datenabrufe und die gesamte Verwaltung der Benutzeroberfläche erfolgen über das clientseitige Skript. JScript oder JavaScript ändert zunächst die Benutzeroberfläche und blendet die DIV-Elemente aus, die die Listendaten und Pagingsteuerelemente enthalten, und zeigt die Schnittstelle "please wait" an. Anschließend werden mithilfe der ausgeblendeten Felder die Informationen für die Webdienst-Methodenparameter gesammelt, und mithilfe des ASP.NET AJAX-Frameworks wird die benutzerdefinierte Webdienstmethode abgerufen.

// This is declared as a global var but could have also been output
// by the Web Part into a hidden field; notice that the InternalName 
// for the list fields must be used.
var fields = new Array("FirstName", "Company", "WorkCity");

// Get the vars containing the data we're going to use.
var url = document.getElementById("siteUrl").value;
var list = document.getElementById("listName").value;
var ps = document.getElementById("pageSize").value;
var ti = document.getElementById("totalItems").value;
var startID = document.getElementById("startID").value;
var endID = document.getElementById("endID").value;
// Some code here to determine the startID for the page.

// Make the call to get the data.
ret = Microsoft.IW.AjaxDataWebService.GetListData(url, list, startID, 
ps, 
   fields, dir, OnComplete, OnTimeOut, OnError);
return true; 

Das Aufrufen eines Webdienstes über ASP.NET AJAX unterscheidet sich von herkömmlichen SOAP-basierten Webdiensten. Zunächst sollte beachtet werden, dass beim Aufrufen einer Webdienstmethode der vollqualifizierte Klassenname verwendet werden muss. Zum anderen werden neben der Bereitstellung aller Parameter für die Webdienstmethode drei zusätzliche Parameter am Ende hinzugefügt. Sie stellen Funktionen in JScript oder JavaScript dar, die aufgerufen werden, wenn die Daten zurückgegeben werden (OnComplete), wenn bei einem Aufruf ein Timeout auftritt (OnTimeOut) oder wenn ein Fehler vorliegt (OnError). In diesem Beispielwebpart werden mit den Funktionen OnTimeOut und OnError nur die Informationen gerendert, die direkt im DIV-Element zurückgegeben wurden, in dem die Daten normalerweise angezeigt werden.

Die OnComplete-Funktion ist unter den drei Parametern der einzige erforderliche Parameter. Hierbei handelt es sich um eine JScript-Funktion, die folgendermaßen aussieht.

function OnComplete(arg)
{
  …
}

Der Parameter arg enthält den Rückgabewert des Webdienst-Methodenaufrufs. In diesem Fall handelt es sich um eine Zeichenfolge mit XML-Code. Für dieses Projekt wird vom Webdienst XML-Code im gleichen Format wie für das XML-Dateninsel-Webpart zurückgegeben. Daher ist der Code zum Auflisten der Daten und zum Rendern auf der Seite nahezu identisch. Zunächst wird ein MSXML DOMDocument erstellt und überprüft, ob gültiger XML-Code zurückgegeben wurde.

// Get the XML DOMDocument. 
var xDoc = createXdoc();
   
// Validate that a document was created.
if (xDoc == null)
{
target.innerHTML = "<font color='red'>A required system component 
(MSXML) is not installed on your computer.</font>";
   return;
}
   
// Load the XML from the Web service method into the document.
xDoc.async = false;
xDoc.loadXML(arg);
   
// Check for parsing errors.
if (xDoc.parseError.errorCode != 0) 
{
   var xErr = xDoc.parseError;
target.innerHTML = "<font color='red'>The following error was 
encountered while loading the data for your selection: " + 
xErr.reason + "</font>";
   return;
}
   
// Get all the items.  
var xNodes;
xDoc.setProperty("SelectionLanguage", "XPath");
xNodes = xDoc.documentElement.selectNodes("/Items/Item");  
   
// Check for errors.
if (xNodes.length == 0)
   target.innerHTML = "<font color='red'>No data was found.</font>";
else
{
   // Code in here is virtually identical to XML island code.
}

Der Renderingcode unterscheidet sich in diesem Fall vom XML-Dateninsel-Webpart nur darin, dass die IDs des ersten und letzten Elements in den ausgeblendeten Feldern startID endID gespeichert werden, um die Pagingfunktionalität des Steuerelements zu unterstützen. Nachdem die Daten durch das Webpart abgerufen wurden, werden sie im entsprechenden DIV-Element gerendert, und Sie können im Inhalt vorwärts- oder rückwärtsnavigieren. In Abbildung 4 und Abbildung 5 sind die ersten beiden Datenseiten dargestellt.

Abbildung 4. Erste Datenseite

Erste Seite des AJAX-Webparts

Abbildung 5. Zweite Datenseite

Beispiel für AJAX-Webpart – zweite Seite

Erstellen eines benutzerdefinierten Webparts mithilfe von JSON

Im vorherigen Beispiel wurde XML-Code von der Webdienstmethode zurückgegeben. Anschließend wurde der Inhalt mithilfe von XPath und einem MSXML DOMDocument gelesen. Eines der leistungsfähigsten Features von ASP.NET AJAX ist jedoch die Möglichkeit, Daten mithilfe von JavaScript Object Notation (JSON) zu nutzen. Auf diese Weise kann der clientseitige Entwickler für den Zugriff auf Daten Objekte und Eigenschaften verwenden, anstatt komplizierten XML-Code zu bearbeiten.

Zur Veranschaulichung wurde eine zweite Webmethode erstellt, mit der eine benutzerdefinierte Klasse zurückgegeben wird. Die zugehörige Methodensignatur sieht wie im folgenden Codebeispiel aus.

[WebMethod]
public Records GetListDataJSON(string webUrl, string listName, int 
startingID, int pageSize, string[] fieldList, string direction)

Die Records-Klasse ist eine benutzerdefinierte Klasse, die zum Speichern der zurückgegebenen Daten entwickelt wurde. Die Definition dieser Klasse sieht wie im folgenden Codebeispiel aus.

[Serializable()]
public class Records
{
   public int Count = 0;
   public int ItemCount = 0;
   public List<Record> Items = new List<Record>();

   public Records()
   {
      // Default constructor.
   }
}

In der Records-Klasse verweist die Count-Eigenschaft auf die Gesamtzahl gefundener Elemente, und die ItemCount-Eigenschaft verweist auf die Anzahl von Elementen, die in diesem Aufruf zurückgegeben werden. Diese Eigenschaften werden für das clientseitige Paging der Daten verwendet. Die eigentlichen Daten, die angezeigt werden, sind in der Items-Eigenschaft enthalten, wobei es sich um eine Liste von Datensatzelementen handelt. Die Record-Klasse ist wie folgt definiert.

[Serializable()]
public class Record
{
   public SerializableDictionary<string, string> Item = 
      new SerializableDictionary<string, string>();

   public Record()
   {
      // Default constructor.
   }
}

Die Record-Klasse weist nur eine einzige Eigenschaft auf, nämlich einen Benutzerwörterbuchtyp, der die Serialisierung unterstützt. Die Serialisierung wird von der Dictionary-Standardklasse in Microsoft .NET Framework 2.0 nicht unterstützt, und standardmäßig werden alle zurückgegebenen Daten von JSON serialisiert. In diesem Fall wird eine Dictionary-Klasse benötigt, damit der Klasse nicht einzelne Eigenschaftennamen zugeordnet werden müssen. Stattdessen können sie als Schlüssel/Wert-Paar hinzugefügt werden. Beispielsweise myValuePair.Item.Add(Schlüssel, Wert).

Hinweis

Die Beschreibung der Benutzerwörterbuchklasse würde den Rahmen dieses Artikels sprengen. Die verwendete Klasse basierte jedoch auf dem Blogbeitrag von Paul Welter zum Thema XML-serialisierbares allgemeines Wörterbuch (in englischer Sprache).

Die Funktionsweise der Webmethode ist mit der XML-Version dahingehend identisch, wie die Daten abgerufen werden. Der Rückgabewert für die Methode wird mithilfe des folgenden Codes erstellt.

Records ret = new Records();
DataTable res = null;

…
// Method is called to retrieve and sort data, and get total number of 
//items.
…

// Set the count of total and returned items.
ret.Count = myInternalWebServiceVariableThatTracksNumItems;
ret.ItemCount = res.Count;

// Enumerate results.
if (res != null)
{
   foreach (DataRowView dr in res)
   {
      // Create a new record.
      Record rec = new Record();

      // Add the ID.
      rec.Item.Add("ID", dr["ID"].ToString());

      // Add each attribute.
      foreach (string oneField in fieldList)
      {
         // Add everything but the ID field.
         if (string.Compare(oneField, "id", true) != 0)
            rec.Item.Add(oneField, dr[oneField].ToString());
      }

      // Add the record to the collection.
      ret.Items.Add(rec);
   }
}
return ret;

Da nun von der Methode eine Klasse zurückgegeben wird, können wir die Daten im clientseitigen Skript mithilfe der Eigenschaften der Klasse durchlaufen. Außerdem müssen wir kein MSXML DOMDocument mehr erstellen, um die Ergebnisse aufzulisten, wodurch unser clientseitiges Skript wesentlich einfacher wird. Der Code zum Rendern der Daten sieht folgendermaßen aus.

// Will hold our output.
var output;
   
// Check for data. Count is a property on the Records class.
if (arg.Count == 0)
   target.innerHTML = "<font color='red'>No data was found.</font>";
else
{
   // Store the total items.
   ti.value = arg.Count;
      
   // Create a table to render the data. Straight
   // HTML goes here.
   …   
      
   // Cycle through all the data. ItemCount is a 
   // property of the Records class.
   for (var i = 0; i < arg.ItemCount; i++)
   {
      // Store page data for the first and last row.
      if (i == 0) 
         startID.value = arg.Items[i].Item["ID"];
      if (i == (arg.ItemCount - 1)) 
         endID.value = arg.Items[i].Item["ID"];
            
      // Create a new row.
      output += "<tr>";
                  
   // Add each cell.
      output += "<td style='width:75px;'>" + 
         arg.Items[i].Item["FirstName"] + "</td>";
      output += "<td style='width:75px;'>" + 
         arg.Items[i].Item["Company"] + "</td>";
      output += "<td style='width:75px;'>" + 
         arg.Items[i].Item["WorkCity"] + "</td>";
         
      // Close the row tag.
      output += "</tr>";
   }

   // The final HTML goes here to close up the TABLE tags.
   …
   
   // Plug the output into the document.
   target.innerHTML = output;
}

Die Benutzeroberfläche ist mit der XML-Version identisch. Das Webpart unterstützt das Vorwärts- und Rückwärtsnavigieren in den Daten. Die Schnittstelle "please wait" wird angezeigt, wenn die Daten geladen werden und wenn ein Benutzer auf die Verknüpfung Next oder Prev klickt.

Messen der Ergebnisse

In einem Netzwerk mit niedriger Bandbreite oder hohen Wartezeiten kann eine solche Lösung sehr positive Auswirkungen auf die belegten Netzwerkressourcen haben. Ein zweites Webpart wurde erstellt, das genau die gleichen Daten aus derselben Liste ausgab. Allerdings wurden alle Daten im Webpart auf dem Server generiert und dann als HTML-Code in die Seite geschrieben. Folglich wurde jedes Mal, wenn auf die Verknüpfung Prev oder Next geklickt wurde, ein Postback an den Server erzwungen, und die gesamte Seite wurde an den Client zurückgesendet. Es sollte unbedingt beachtet werden, dass genau dies auch passiert wäre, wenn wir stattdessen ein UpdatePanel-Steuerelement von ASP.NET AJAX verwendet hätten. Alle Formularvariablen für die Seite werden an den Server zurückgesendet, und die gesamte Seite wird von der Anforderung zurückgegeben. Jedoch wird nur der Teil der Seite, der in UpdatePanel enthalten ist, aktualisiert. Abbildung 6 zeigt einen in Fiddler aufgenommenen Snapshot der Anforderung nach dem Klicken auf die Verknüpfung Next.

Abbildung 6. Anforderungen durch Klicken auf die Verknüpfung Next link im zweiten Webpart

Fiddler-Ergebnisse für Webpart

Der Fiddler-Snapshot zeigt, dass für die Anforderung und Antwort eines Postbackrenderings der Listendaten insgesamt 79.424 Bytes über das Netzwerk gesendet wurden. Alternativ zeigt Abbildung 7 einen Fiddler-Snapshot, wobei das ASP.NET AJAX-fähige Webpart zum Abrufen derselben Daten über den benutzerdefinierten Webdienst mithilfe von XML verwendet wurde.

Abbildung 7. Webpart hat dieselben Daten über den benutzerdefinierten Webdienst mithilfe von XML abgerufen

Fiddler-Ergebnisse für benutzerdefinierten Webdienst

Die gleichen Listendaten wurden abgerufen, aber nur 1.973 Bytes wurden über das Netzwerk gesendet. Das macht einen gewaltigen Unterschied. Durch die intelligente Verwendung dieser Methode konnte der Netzwerkverkehr deutlich reduziert werden. Die kleinste Nutzlast ergab sich allerdings mithilfe der Webdienstmethode, bei der JSON zum Zurückgeben der Records-Klasse verwendet wird (siehe Abbildung 8).

Abbildung 8. Zurückgeben der Records-Klasse mithilfe von JSON

Fiddler-Ergebnisse für JSON

Schlussfolgerung

Mit JSON konnte die gesendete Gesamtnutzlast für eine Anforderung auf 1.817 Bytes reduziert werden, was einer Reduktion von 98 % bei der Anforderungsgröße für das Webpart entspricht, mit dem ein vollständiges Postback zum Abrufen der Daten und zum Navigieren in diesen Daten ausgeführt wird. Außerdem konnten wir die Größe von ECMAScript (JScript, JavaScript) zum Auflisten der Daten reduzieren sowie dabei auch den Code vereinfachen.

Die Entwicklung einer solchen Lösung ist komplizierter, wenn für Ihre Website Einschränkungen hinsichtlich Bandbreite oder Wartezeiten gelten. Durch diese Vorgehensweise kann allerdings die Leistung und die Benutzerfreundlichkeit verbessert werden.

Zusätzliche Ressourcen

Weitere Informationen finden Sie in den folgenden Ressourcen:

Herunterladen dieses Buchs

Dieses Thema wurde zum leichteren Lesen und Ausdrucken in das folgende Buch zum Herunterladen aufgenommen:

Die vollständige Liste der verfügbaren Bücher finden Sie unter Bücher zum Herunterladen für Office SharePoint Server 2007.