Kolumne: SharePoint ganz praktisch

Umfangreiche Listen und übergreifende Listenabfragen
Kommentare

Die heutige Ausgabe der Kolumne erläutert, wie umfangreiche Listen mit zahlreichen Einträgen programmatisch verarbeitet werden können. Weiterhin geht es um die Abfrage von Listendaten über mehrere Sites. In der vorherigen Ausgabe der Kolumne wurde der Zugriff auf Listendaten über das serverseitige Objektmodell dargestellt. Wie gezeigt wurde, sind Zugriffe sowie Manipulationen von Listendaten sehr einfach möglich. Solange die Listen nur wenige Einträge besitzen, muss beim Zugriff auf die Einträge nichts Besonderes beachtet werden. Die Verarbeitung der Listenelemente erfolgt entweder über GetItemById oder über Items. Besitzt die Liste jedoch sehr viele Einträge, müssen einige Dinge beim programmatischen Zugriff berücksichtigt werden.

Limitierung durch SharePoint

Umfangreiche SharePoint-Listen mit sehr vielen Einträgen sind keine Seltenheit. Wird auf diese Listen programmatisch zugegriffen, muss die maximale Ergebnismenge reduziert werden. Dies ist zwingend notwendig, da SharePoint den Abfrageschwellenwert für (Listen-)Ergebnisse auf 5 000 Elemente festlegt. Wenn die Abfrage mehr Elemente zurückgibt als im Abfrageschwellenwert konfiguriert ist, 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 Standardwert, der über die Zentraladministration (Central Administration) pro Webanwendung (Web Application) justiert werden kann. Um die Limitierungen einer Webanwendung einzusehen, muss zunächst über die Zentraladministration und „Manage Web Applications“ zu der Übersicht aller Webanwendungen navigiert werden. Dann muss die gewünschte Anwendung aus der Liste selektiert werden. Im oberen Menüband (Ribbon) befindet sich der Menüpunkt „General Settings“, und über den Punkt „Resource Throttling“ können nun die Begrenzungen bzw. Limitierungen eingesehen werden. Abbildung 1 zeigt einen Ausschnitt der wichtigsten Grenzwerte für die Verarbeitung von Ergebnismengen. Der Wert „List View Threshold“ bestimmt die Anzahl an Datenbankeinträgen, die bei einer Abfrage maximal zurückgegeben werden dürfen. Wird der festgelegte Wert überschritten, bricht die Abfrage mit einer Fehlermeldung ab. Mit der Einstellung „Object Model Override“ kann erreicht werden, dass der zuvor festgelegte maximale Wert temporär verändert werden darf. Dies ist aber nur möglich, wenn eine programmatische Abfrage erfolgt und der aktuell aktive Benutzer genügend Rechte dazu besitzt. Diese Einstellung ist sinnvoll, wenn während einer automatischen Stapelverarbeitung viele Einträge zu verarbeiten sind. Die zusätzliche Einstellung „List View Threshold for Auditors and Administrators“ ermöglicht es, für bestimmte Benutzergruppen einen eigenen Grenzwert für die maximale Trefferanzahl festzulegen. In der Regel ist dieser Wert höher und im Standard auf 20 000 gesetzt. Die letzte dargestellte Einstellung „List View Lookup Threshold“ gibt die maximale Anzahl an verknüpften Datenfeldern an, die während einer Datenabfrage berücksichtigt bzw. aufgelöst werden können.

Abb. 1: Konfiguration von SharePoint-Limitierungen

Zugriff auf umfangreiche Listen und ihre Verarbeitung

Immer wenn möglich, sollte die Anzahl der über einen API-Zugriff abgerufenen Listenelemente eingeschränkt werden. Dazu kann am einfachsten eine Collaborative-Application-Markup-Language-Abfrage (kurz: CAML Query) zur Laufzeit verwendet werden. Listing 1 zeigt, wie die Trefferanzahl beim Zugriff auf eine Aufgabenliste reduziert werden kann. Anstatt alle Einträge zu verarbeiten, werden über die definierte CAML-Einschränkung nur jeweils die Aufgaben mit dem Status ungleich „Completed“ eines bestimmten Benutzers zurückgegeben. CAML-Abfragen können sehr komplex werden und verschachtelte Bedingungen enthalten. Der syntaktische und schematische Aufbau ist unter [1] einsehbar. Teilweise ist es aber nicht möglich, die Anzahl der Listeneinträge auf eine übersichtliche Trefferanzahl zu reduzieren. Überschreitet die Treffermenge die oben erläuterten Limitierungen, kommt es zu Fehlern. In solchen Fällen muss seitenweise (per Paging) durch die Ergebnismenge navigiert werden. Wie dazu vorzugehen ist, zeigt Listing 2. Wichtig ist hier das über die Eigenschaft ListItemCollectionPosition abzurufende Token. Es stellt in diesem Kontext eine Markierung dar. Diese kommt zur Ermittlung des nächsten zu ladenden Datenausschnitts zum Einsatz. Die Schleife durchläuft die Ergebnismenge solange, bis kein neuer Token-Wert zurückgeliefert wird. Dieses Vorgehen ermöglicht die sichere und fehlerfreie Verarbeitung umfangreicher Listentreffer.

public void ReadListContentCAMLQuery()
{
  using (SPSite site = new SPSite(YOUR PORTAL URL))
  {
    using (SPWeb web = site.OpenWeb())
    {
      SPList tasks = web.Lists["Tasks"];
      SPQuery query = new SPQuery();
      query.Query = "
      
        
          
            
            
              labserveradministrator
            
          
          
            
            
              Completed
            
          
        
      ";
      SPListItemCollection runningTasks = tasks.GetItems(query);
      foreach (SPListItem item in runningTasks)
      Console.WriteLine("{0} -> {1}", item.Title, item["Status"]);
    }
  }
}
public static void PagingResult()
{
  using (SPSite site = new SPSite(YOUR PORTAL URL))
  {
    using (SPWeb web = site.OpenWeb())
    {
      SPList cstList = web.Lists["My Tasks"];
      SPQuery query = new SPQuery();
      query.RowLimit = 1;
      query.Query = "";
      SPListItemCollection result = null;
      do
      {
        result = cstList.GetItems(query);
        foreach (SPListItem item in result)
          Console.WriteLine("{0} -> {1}", item.Title, item["Status"]);
        query.ListItemCollectionPosition = result.ListItemCollectionPosition;
      } while (query.ListItemCollectionPosition != null);
    }
  }
} 

ContentIterators

Das SharePoint-Server-2010-API wurde um die Klasse ContentIterator erweitert, um die Abfrage umfangreicher Datenmengen effizient und ressourcenschonend durchzuführen. Zu finden ist die neue Klasse im Namensraum Microsoft.Office.Server.Utilities. Durch die Verwendung der ContentIterator-Klasse entfällt die Notwendigkeit einer einfachen Schleife, um durch Ergebnismengen zu iterieren. Laut Definition ermöglicht die neue Klasse die optimierte Abfrage umfangreicher Listen und reguliert dabei automatisch die dafür notwendige Datenmenge. Dabei wird nicht nur die Abfrage von Listeneinträgen unterstützt, sondern die Klasse kann auch über Listen und Sites (Seiten) iterieren. Durch den optimierten Abfragemechanismus verhindert die Klasse eventuell auftretende Ausnahmen des Typs SPQueryThrottledException zwar nicht vollständig, bietet aber einen besseren Schutz gegenüber der einfachen Schleifeniteration über die Ergebnismenge. Die Verwendung der Klasse ist nicht sonderlich kompliziert (Listing 3). Zunächst muss eine Instanz der gewünschten Liste angelegt werden. Das Listenobjekt wird anschließend der Methode ProcessListItems übergeben. Für jeden abgerufenen Listeneintrag wird dann eine Callback-Methode aufgerufen, der jeweils ein Listeneintrag übergeben wird. Innerhalb der Methode kann der Listeneintrag je nach Anforderung verarbeitet werden. Zu beachten ist allerdings, dass es auch bei der Verwendung des ContentIterators zu SPQueryThrottledException-Ausnahmen kommen kann. Dies ist aus der Arbeitsweise der Klasse ableitbar. Intern teilt der ContentIterator die Abfrage in mehrere Datenpakete ein, die nacheinander ebenfalls über ein SPQuery-Objekt abgerufen werden. Auf die intern verwendete und erstellte SPQuery-Instanz kann jedoch nicht direkt zugegriffen werden. Um dennoch Einfluss auf sie zu bekommen, kann der Methode ProcessListItems eine eigene SPQuery-Instanz übergeben werden. So ist es möglich, eigene Werte einzustellen, z. B. für die RowLimit-Eigenschaft. Zudem sollte die Abfrage einen gültigen Sortierausdruck besitzen. Dieser kann der Abfrage leicht über die in der Klasse ContentIterator definierte Konstante ItemEnumerationOrderByNVPField hinzugefügt werden.

public static void ContentIterator()
{
  using (SPSite site = new SPSite(YOUR PORTAL URL))
  {
    using (SPWeb web = site.OpenWeb())
    {
      SPList cstList = web.Lists["My Tasks"];
      ContentIterator contentIterator = new ContentIterator();
      contentIterator.ProcessListItems(cstList,
        delegate(SPListItem item)
        {
          Console.WriteLine("{0} -> {1}", item.Title, item["Status"]);
        },
        delegate(SPListItem item, Exception error)
        {
          Console.WriteLine("Error: {0}", error.Message);
          return true;
        }
      );
    }
  }
} 

SPSiteDataQuery

In den bisherigen Betrachtungen wurden direkt Abfragen auf konkrete Listen ausgeführt. Neben dem direkten Zugriff auf eine bestimmte Liste ist es aber auch möglich, mehrere Listen über Websites hinweg abzufragen. Dazu kommt die Klasse SPSiteDataQuery [2] zum Einsatz. Sie ermöglicht die Abfrage von mehreren Listen in unterschiedlichen Websites in derselben Websitesammlung (Listing 4). Abgefragt werden hier alle Aufgabenlisten, die in der aktuellen Webseitenhierarchie gefunden werden. Der gewünschte Listentyp ist über die Eigenschaft Lists anzugeben. Die möglichen Werte für das Attribut ListTemplate sind unter [3] erhältlich. Eine weitere wichtige Eigenschaft ist Webs. Hierüber wird vorgegeben, welche Ebenen während der Abfrage berücksichtigt werden sollen. Mögliche Werte für das Scope-Attribut sind Recursive und SiteCollection. Im Standard wird nur die Ebene berücksichtigt, von der die Abfrage ausgeführt wird. Wird Scope auf Recursive gesetzt, werden die aktuelle Site sowie alle untergeordneten Sites miteinbezogen. Die Einstellung SiteCollection hingegen bewirkt, dass die Abfrage immer von der Wurzelwebsitesammlung aus startet und rekursiv alle Sites berücksichtigt. Zusätzlich ermöglicht die Klasse, die Limitierungen der Treffermenge optional für bestimmte Benutzergruppen zu aktivieren bzw. zu deaktivieren. Dies ist über die Eigenschaft QueryThrottleMode möglich, die die Werte Default (Standard), Override (Überschreiben) und Strict (Streng) annehmen kann.

Die vorgenommene Einstellung interagiert mit den aktuellen Benutzerrechten, unter denen die Abfrage ausgeführt wird. Führt ein lokaler Administrator die Abfrage im Default-Modus aus, gelten keine Einschränkungen. Für normale Benutzer hingegen gelten die weiter oben beschriebenen Limitierungen. Im Override-Modus gilt für lokale Administratoren das gleiche Verhalten wie im Default-Modus. Wenn die Webanwendungsrichtlinie volle Lese- und Kontrollrechte für Benutzer eingeräumt hat, werden für Auditoren und Administratoren die definierten Limitierungen angewendet. Die Beschränkungen für die Anzahl der Lookups, Personen/Gruppen und Workflow-Statusfelder fallen hingegen weg. Im Strict-Modus gelten für alle Benutzer – unabhängig ihrer Berechtigungsstufe – die vorgegebenen Limitierungen.

public static void UsingDataQuery()
{
  using (SPSite site = new SPSite(YOUR PORTAL URL))
  {
    using (SPWeb web = site.OpenWeb())
    {
      SPSiteDataQuery query = new SPSiteDataQuery();
      query.Lists = "";
      query.ViewFields = "
      ";
      query.Query = " 

Zusammenfassung

In der heutigen Kolumne wurden einige wichtige Techniken vorgestellt, wie umfangreiche Listeneinträge verarbeitet werden können. Bei jeder Lösung sollte immer im Vorfeld geklärt werden, ob eine Abfrage in Zukunft mehr als die erlaubten Treffer zurückgeben kann. Denn typischerweise treten Probleme meist während des produktiven Betriebs auf, wenn mehr als die klassischen hundert Testeinträge in einer Liste vorhanden sind. Die Beschränkung der Treffermenge mittels CAML-Abfrage sollte wenn möglich immer vorgenommen werden. Da CAML-Abfragen aber sehr komplex und unübersichtlich werden können, wird in einer zukünftigen SharePoint-Kolumne LINQ to SharePoint vorgestellt. Über diese Technologie ist ein vereinfachter und typisierter Zugriff auf Listendaten möglich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -