Kolumne: SharePoint ganz praktisch

Einfacher Zugriff auf Listendaten
Kommentare

Im letzten Teil der SharePoint-Kolumne wurde das serverseitige Objektmodell vorgestellt. Die heutige Ausgabe widmet sich im Speziellen den Zugriffstechniken, um Listendaten in SharePoint zu verarbeiten.

Wie in der vorherigen Kolumne deutlich wurde, besitzt das serverseitige Objektmodell ein umfangreiches API, um alle SharePoint-Aufgaben automatisieren und programmgesteuert ausführen zu können. Die mitunter wichtigste Klasse – und ich behaupte die am häufigsten zum Einsatz kommende – ist die SPList-Klasse, die den Zugriff auf Listendaten ermöglicht [1]. Die Rolle von Listen in eigenen SharePoint-Anwendungen ist vergleichbar mit der Rolle von Datenbanktabellen in herkömmlichen Lösungen. Wie in einer späteren Ausgabe noch erläutert werden soll, unterscheiden sich Datenbanktabellen von SharePoint-Listen jedoch erheblich.

SharePoint ist listenartig organisiert

Der gesamte Inhalt eines SharePoint-Portals ist listenartig organisiert. Die meisten, wenn nicht gar alle Inhalte befinden sich also in irgendeiner Art von Liste. Früher oder später muss daher wahrscheinlich jeder SharePoint-Entwickler auf eine SharePoint-Liste programmatisch zugreifen. Das serverseitige SharePoint-Objektmodell bietet dafür über die vorhandenen Methoden vielfältige Manipulationsmöglichkeiten, der Zugriff auf Listen wird dadurch problemlos möglich. Hierbei werden alle typischen CRUD-Datenoperationen (Create, Read, Update und Delete) unterstützt. Es können aber nicht nur vorhandene Listen verwendet werden. Ebenso unterstützt das API die Neuanlage, das Löschen und die Änderung bestehender Listen. Listing 1 zeigt zunächst, wie einfach Listendaten ausgelesen werden können. In dem Beispiel werden zunächst alle Listenelemente gelesen und innerhalb einer Schleife auf der Konsole ausgegeben. Oft wird jedoch ein spezieller Listeneintrag benötigt. Dafür können die Methoden GetItemById oder GetItemByUniqueId zum Einsatz kommen, die den gezielten Zugriff auf einen bestimmten Listeneintrag ermöglichen.

public static void AccessAndReadListItems()
{
  using (SPSite site = new SPSite("PORTAL-URL"))
  {
    using (SPWeb web = site.OpenWeb())
    {
      SPList tasks = web.Lists["Tasks"];
      foreach (SPListItem item in tasks.Items)
        Console.WriteLine("{0} -> {1}",item.Title, item["Status"]);
    }
  }
}

Listenelemente bearbeiten, erstellen und löschen

Das Beispiel in Listing 2 demonstriert die typischen Zugriffe auf Listenelemente. Hier wird zunächst ein neues Listenelement angelegt und anschließend sofort wieder gelöscht. Nachfolgend wird ein bestehendes Listenelement geladen und aktualisiert. Da das SharePoint-API für jeden dieser Vorgänge bereits vorgefertigte Methoden bietet, gestaltet sich der Zugriff auf Listen sehr einfach und übersichtlich. Zu beachten sind die zwei verwendeten Möglichkeiten, wie auf einzelne Felder eines Listeneintrags zugegriffen werden kann. Bei der oberen Neuanlage eines Eintrags wird zur Adressierung der Felder die Auflistung SPBuiltInFieldId verwendet. Bei der darauf folgenden Aktualisierung eines bestehenden Eintrags wird die Spalte Title über den Anzeigename adressiert. Generell ist die erste Adressierungsvariante über die Auflistung SPBuiltInFieldId bei eingebauten Feldern vorzuziehen. Wenn das betroffene Feld nicht über die Auflistung ansprechbar ist, kann das Feld wahlweise über den Namen oder den GUID referenziert werden. Werden Spalten über den Namen referenziert, werden sie auch dann noch gefunden, wenn der Anzeigename später umbenannt wird. Wird die Index-Methode item[] mit einer Zeichenkette aufgerufen, sucht die Methode zunächst ein passendes Feld mit übereinstimmenden internen Namen. Wird über diese Suche kein Feld gefunden, wird der Anzeigename ausgewertet. Um die Funktionsweise der Index-Methode besser verstehen zu können, zeigt Listing 3 eine typische Definition eines SharePoint-Felds. Wie zu erkennen ist, besitzt ein Feld neben einem GUID und dem Anzeigenamen noch einen internen Namen (und viele weitere Eigenschaften). Idealerweise ändern sich der interne Name und der GUID im Attribut ID für ein Feld nicht.

public static void UpdateListItem()
{
  using (SPSite site = new SPSite("[PORTAL-URL]"))
  {
    using (SPWeb web = site.OpenWeb())
    {
      SPList tasks = web.Lists["Tasks"];
      SPListItem newItem = tasks.Items.Add();
      newItem[SPBuiltInFieldId.Title] = "Neuer Eintrag";
      newItem[SPBuiltInFieldId.TaskStatus] = SPResource.GetString
        (new CultureInfo((int)web.Language, false), 
         "WorkflowTaskStatusInProgress", new object[0]);
      newItem.Update();
      ...
      newItem.Delete();
      SPListItem singleTask = tasks.GetItemById(2);
      singleTask["Title"] = "Updated!";
      singleTask.Update();
    }
  }
}
http://schemas.microsoft.com/sharepoint/v3"
        StaticName="ContentTypeId"
        ...weitere Attribute ausgelassen ...
        DisplayName="$Resources:core,Content_Type_ID;"
    

Abfrage umfangreicher Listen

Dass SharePoint-Listen viele Einträge besitzen, ist keine Seltenheit. Wird auf diese Listen programmatisch zugegriffen, muss die maximale Ergebnismenge reduziert werden. Das ist notwendig, da SharePoint den Abfrageschwellenwert für (Listen-)Ergebnisse auf 5 000 Elemente festlegt. Gibt die Abfrage mehr Elemente zurück als im Abfrageschwellenwert konfiguriert, wird die Abfrage blockiert und es werden keine Ergebnisse zurückgegeben. Die Begrenzung auf 5 000 Einträge pro Abfrage ist allerdings nicht statisch und kommt auch nicht immer zum Einsatz. Hierbei handelt es sich um einen Konfigurationswert, der über die Zentraladministration (Central Administration) pro Webanwendung (Web Application) justiert werden kann. Wie dennoch alle Listeneinträge verarbeitet werden können, wird im weiteren Verlauf erläutert.

SPQuery: Ergebnismenge reduzieren

Im Laufe der Zeit wachsen SharePoint-Listen an und können unter Umständen mehrere Millionen Einträge enthalten. Aus diesem Grund gehört es zu den Best Practices, solche Listen zu begrenzen, die per SPQuery abgefragt werden. Als optimale Begrenzungsmenge haben sich 2 000 Listeneinträge etabliert. Erfolgt keine Begrenzung der Abfragemenge, kann das je nach Konstellation zu einem Fehlverhalten führen, wie bereits oben erläutert. Abbildung 1 zeigt zur Verdeutlichung die entsprechende Fehlermeldung. Listing 4 zeigt, wie Listenergebnisse seitenweise abgerufen werden können, um solche Fehler zu vermeiden. Hierbei wird über die Eigenschaft RowLimit eine maximale Treffermenge von 2 000 Einträgen pro Seite definiert. Die definierte do-Schleife wird so lange durchlaufen, bis keine Einträge mehr zurückgegeben werden. Innerhalb der inneren foreach-Schleife erfolgt die Listenabfrage. Die Syntax und Verwendung von SPQuery ähneln den bereits gezeigten Beispielen. Neu ist die Verwendung der Eigenschaft ListItemCollectionPosition. Sie definiert die aktuelle Indexposition, ab der Daten aus der Abfrage zurückgegeben werden sollen. Da nun seitenweise durch die Treffermenge navigiert wird, darf die Sortierung der Elemente nicht vergessen werden. Durch die Begrenzung der maximalen Ergebnismenge wird die Verarbeitungsgeschwindigkeit verbessert und eventuelle Probleme, die bei der Überschreitung des Abfrageschwellenwerts auftreten könnten, werden bereits im Vorfeld vermieden.

Abb. 1: Rückgabe zu vieler Treffer aus einer SharePoint-Abfrage

private static void PagingResult()
{
  using (SPSite site = new SPSite("PORTAL-URL"))
  {
    using (SPWeb web = site.OpenWeb())
    {
      SPList cstList = web.Lists["LargeList"];
      SPQuery query = new SPQuery();
      query.RowLimit = 2000;
      query.Query = 
        "";
      SPListItemCollection result = null;
      do
      {
        result = cstList.GetItems(query);
        foreach (SPListItem item in result)
          Console.WriteLine("{0} -> {1}", item.Title, item["Eintrag"]);
        query.ListItemCollectionPosition = 
          result.ListItemCollectionPosition;
      } while (query.ListItemCollectionPosition != null);
    }
  }
} 

SPQuery intern

Die Notwendigkeit zur Eingrenzung der Abfragemenge wird am deutlichsten, wenn die konkreten Datenbankoperationen ausgewertet werden. Wie in Listing 5 erkennbar, werden in diesem Fall alle bzw. die maximale Menge von 2 147 483 648 Einträgen mit einer einzigen Leseoperation angefordert. Wird dagegen die Eigenschaft RowLimit mit einer gültigen Sortierung verwendet, reduziert sich die Datenmenge erheblich (Listing 6). Aus diesem Grund ist es immer empfehlenswert, die Ergebnismenge im Vorfeld zu reduzieren. Neben der Verwendung der Eigenschaft RowLimit sollte auch die mögliche Ergebnismenge über Filterkriterien (where-Bestandteil von SPQuery) so weit wie möglich eingegrenzt werden.

exec sp_executesql N'DECLARE @DocParentIdForRF ...  SELECT TOP(@NUMROWS) t1.[Type] ... @NUMROWS bigint, ... ,@NUMROWS=2147483648, ...
exec sp_executesql N'DECLARE @DocParentIdForRF ... SELECT TOP(@NUMROWS) t1.[Type] ... ,@NUMROWS bigint,...,@NUMROWS=11,..

Collaborative Application Markup Language

Wie anhand der oberen Erläuterung deutlich wurde, ist es zwingend erforderlich, die Ergebnismenge von Abfragen zu reduzieren. Das kann auf zwei Wegen erfolgen: Entweder wird auf eine vorgefertigte Abfrage zurückgegriffen oder es wird eine temporäre Abfrage mittels CAML (Collaborative Application Markup Language) erstellt. Die Verwendung einer zuvor programmatisch oder manuell erstellten Ansicht (SPView) für eine Listenabfrage ist simpel. Das folgende Beispiel verwendet eine Aufgabenliste und geht davon aus, dass über die SharePoint-Oberfläche im Vorfeld eine Ansicht namens „In Bearbeitung“ erstellt wurde. Die neue Sicht wird so konfiguriert, dass nur Aufgaben angezeigt werden, die zum Zeitpunkt der Abfrage den Status „In Bearbeitung“ besitzen. Listing 7 demonstriert, wie die Abfrage beim Zugriff auf die Liste verwendet werden kann. Die Klasse SPList enthält die Methode GetItems, die mehrere Überladungen besitzt. Unter anderem kann dieser Methode ein SPView-Objekt übergeben werden, das eine vorhandene Sicht (View) der Liste repräsentiert. Ein gültiges SPView-Objekt kann sehr leicht über die Eigenschaft Views der SPList-Instanz erlangt werden. Diese Eigenschaft stellt eine Auflistung aller Sichten dar, die der entsprechenden Liste zugeordnet sind. Im Beispiel wird das gewünschte SPView-Objekt über den Titel der View ermittelt. Unterstützt wird auch der Zugriff über den Index oder des GUID der entsprechenden View. Nachdem die GetItems-Methode so konfiguriert wurde, liefert sie nach dem Aufruf nur die Listeneinträge zurück, die auch die entsprechende View an der Oberfläche zurückgibt. Bei der programmatischen Verwendung einer SharePoint-Listensicht, wie sie hier gezeigt wurde, besteht immer die Gefahr, dass ein Benutzer mit ausreichenden Rechten die View löscht. Daher ist es empfehlenswert, die benötigte CAML-Abfrage im Code zu verwalten. Wie das geht, wird im nächsten Teil der Kolumne demonstriert.

public static void ReadListContentExistingQuery()
{
  using (SPSite site = new SPSite("URL"))
  {
    using (SPWeb web = site.OpenWeb())
    {
      SPList tasks = web.Lists["Tasks"];
      SPListItemCollection runningTasks =
        tasks.GetItems(tasks.Views["In Bearbeitung"]);
      foreach (SPListItem item in runningTasks)
        Console.WriteLine("{0} -> {1}", item.Title, item["Status"]);
    }
  }
} 

Zusammenfassung

Die heutige Ausgabe der Kolumne widmete sich dem einfachen Zugriff auf SharePoint-Listendaten. Weitere Informationen zu den beiden wichtigsten Klassen SPList und SPListItem sind unter [1] und [2] verfügbar. Im nächsten Teil geht es dann darum, wie umfangreiche Listendaten effektiv verarbeitet und Informationen über die gesamte Websitehierarchie gesammelt werden können. Weiterhin wird demonstriert, wie LINQ und CAML bei der Verarbeitung von Listeneinträgen zum Einsatz kommen.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -