CAML-Queries ohne Frust schreiben

CAML ist eine relativ mächtige Abfragesprache. Wenn auch nicht so mächtig wie SQL, ist es dennoch möglich relativ komplexe Abfragen zu realisieren.
Die größte Hürde – wenn ich jetzt mal davon ausgehe, dass es ein grundsätzliches Verständnis über die Abfrage von Daten gibt – ist die Syntax selbst. Nicht dass diese unmöglich komplex wäre, verhaspelt man sich jedoch gerne bei verschlungenen WHERE-Abfragen oder Abfragen, welche über mehr als nur eine Liste gehen.

Da ich versuche so pragmatisch wie nur irgendwie möglich zu arbeiten bin ich immer auf der Suche einen komplexen Sachverhalt zu vereinfachen. Lieber ein Tool mehr, als einen Auftrag weniger.

Ein Tool das diesen pragmatischen Ansatz perfekt umsetzt ist LINQPad (http://www.linqpad.net/).

Wer dieses Tool noch nicht kennt, der MUSS sich dieses schicke Ding einfach einmal ansehen!

Ich werde in diesem Beitrag keine LINQ oder LINQPad Einführung schreiben, dies würde den Rahmen diese Blog-Eintrages bei Weitem übersteigen. Es gibt im Tool selbst genügend Beispiele in welchen die Funktionsweise von LINQ und LINQPad genauestens beschrieben wird.

Aber was hat nun LINQPad mit CAML zu tun!?

Nun, erst mal gar nichts! LINQPad ist aber erweiterbar, der Funktionsumfang kann durch Extensions erweitert werden.

Eine dieser Extensions nennt sich „LINQPad Data Context Driver for SharePoint 2010“ (http://linqpadsp2010driver.codeplex.com/). Diese Extension verwendet LINQ2SharePoint, welche neu mit SharePoint 2010 eingeführt wurde. Und genau dieses LINQ2SharePoint hat sie in sich, die CAML-Macht!

Bevor man nun LINQPad runterlädt und diese Extension installiert noch folgender Hinweis: Man muss LINQPad herunterladen, welches noch mit der .NET Version 3.5 kompiliert wurde, aus dem einfachen Grund, dass SharePoint noch auf dieser Version aufsetzt.

Wie bekomme ich das Ganze zum laufen?

Es sei vorausgeschickt, dass dieser „LinqPad Data Context Driver“ auf das Serverobjektmodell von SharePoint zugreift und somit lokal ein SharePoint laufen muss. Ob das nun eine Entwicklerumgebung oder die Liveumgebung ist, sei dem Leser überlassen. Ich übernehme bei etwaigen Schäden am Datenbestand jedoch keinerlei Haftung!

  1. LINQPad for .NET Framework 3.5 (nicht 4.0!) herunterladen
  2. In einen Ordner deiner Wahl extrahieren
  3. „LinqPad Data Context Driver for SharePoint 2010“ herunterladen
  4. In einen Ordner deiner Wahl extrahieren, kann auch der Ordner von LINQPad selbst sein. Es sollte sich dabei um eine .LPX-Datei handeln.
  5. LINQPad starten
  6. Auf „Add connection“ klicken
  7. View more drivers
  8. Browse to a .LPX file
  9. Die extrahierte .LPX-Datei wählen (zum Zeitpunkt dieses Blog-Eintrages ist das die „LinqPad-SP-Driver.2.0.lpx“)
  10. Bestätigen, dann sollte die Meldung kommen, dass der Treiber korrekt installiert wurde.
  11. Nun hat man zwei Möglichkeiten
    Einen automatisch generierten DataContext generieren
    Einen typisierten DataContext wählen, welcher z.B. mithilfe von spmetal.exe erstellt wurde. Eine gute Einführung dazu, in feinster Weihnachtsoptik verpackt, findet sich hier: http://sharepointadvent.de/2011/12/04/linq-2-sharepoint/
  12. Da es mir nicht gelungen ist den DataContext dynamisch generieren zu lassen habe ich mich dazu entschlossen den typisierten/statischen DataContext zu benutzen. Dieser ist, wenn auch nicht ganz so pragmatisch, meist sowieso der bessere Ansatz, da man zusätzlich über spmetal Parameter angeben kann, welche Listen, Inhaltstypen und Felder überhaupt von Interesse sind.
  13. Das Generieren der Assembly mittels spmetal lasse ich mal außen vor. Grundsätzlich ist eine Codedatei mittels spmetal zu generieren, danach kann diese mit Visual Studio, msbuild oder csc in eine .NET-Library kompiliert werden (Achtung: .NET 3.5 verwenden!)…
  14. Wenn dies geschehen ist „Static SharePoint 2010 Driver“ wählen
  15. Zur genierten Assembly browsen, den DataContext wählen und die SharePoint URL eingeben, auf welche sich der Context bezieht.
  16. Nun sollte im LINQPad eine neue Connection zur Verfügung stehen

Aber was bringt mir das jetzt?

Nun, man kann LINQ-Abfragen auf SharePoint absetzen und sich die generierte CAML-Query herauspicken! Das tolle ist, dass diese Queries auch JOINS beinhalten können.

Am besten ist es wohl, dies anhand eines Beispiels zu zeigen:

In meiner Firma wird die Abwesenheitsverwaltung mittels SharePoint abgewickelt. Ich lasse mal die Komplexität so einer Verwaltung außen vor (ich sage nur, horizontale und vertikale Teilzeiten!) und stelle das Augenmerk auf eine vereinfachte Darstellung:

  • Der Mitarbeiter stellt einen Urlaubsantrag, welcher von einer autorisierten Person (es muss nicht immer der Abteilungsleiter sein) zu genehmigen ist. Falls diese Person nicht da ist, kann eine andere autorisierte Person ausgewählt werden.
  • Nun möchten diese autorisierten Personen gerne eine Übersicht darüber haben, welcher Mitarbeiter wann Abwesend ist.
  • Damit so eine Ansicht generiert werden kann ist folgende Abfrage zu erstellen:
    „Hole mir alle Abwesenheiten, welche genehmigt wurden von mir selbst und von den Mitarbeitern, welche mich als genehmigende Person oder als Ersatz der genehmigenden Person hinterlegt haben.“
    Zusätzlich gibt es noch die Anforderung, nur Abwesenheiten anzuzeigen, welche seit dem 1.1.2012 getätigt wurden, fragt mich jetzt nicht wieso, es ist halt so, kein Witz…
  • Die Information zur genehmigenden Person ist nicht in der Abwesenheit selbst hinterlegt, sondern beim Wochenschema des Mitarbeiters, also ist ein JOIN vonnöten.

So eine Abfrage in CAML zu definieren ist nicht gerade trivial. Und hier hilft einem LINQ gewaltig!

Hier die LINQ-Abfrage der oben genannten Anforderung:

Die generierte CAML Query sieht man, wenn man auf SQL (!) klickt.

CAML-Puristen werden nun behaupten: „Die CAML-Query sieht ja schrecklich aus, ich kann das doch viel besser!“, der Pragmatist wird bei dieser Aussage aber schmunzeln und behaupten: „Aber ich hab noch alle Haare auf dem Kopf!“. Okay, zumindest diejenigen die nicht an androgenetischer Alopezie leiden, können dies behaupten…

Also mir reicht das dargebotene CAML vollkommen, und eine Schönheitskur erfährt es, indem ich es als XML-Datei in Visual Studio einfüge und es anschließend formatieren lasse.
Nun nur noch schnell die konstanten Werte (bei mir die 2) mit dem CAML-Pendant <UserID Type=“Integer“/> ersetzen und schon kann diese View über SharePoint Designer in einem XsltListViewWebPart eingefügt werden:

Die Query sieht im Designer wie folgt aus (Eventuell kann man auch hier den konstanten Wert mit dem aktuellen Benutzer, oder einem Parameter, austauschen):

Und schon hat man seine (relativ komplexe) Listenansicht! Ein normaler Mitarbeiter sieht nur seine Abwesenheiten, eine autorisierte Person sieht zusätzlich zu seinen Abwesenheiten auch noch die Abwesenheiten seiner „untergeordneten“ Mitarbeiter.

Ein Wort zum select-Statement (bzw. ViewFields).

In den meisten Fällen wird es eine Fehlermeldung in der Results-Ausgabe geben, wenn man einfach das Objekt zurückgibt (im Beispiel select a). Dies ist deshalb begründet, dass versucht wird das Objekt mit seinen referenzierten Objekten zu deserialisieren, was nicht immer gelingt (manchmal klappt es nach mehrmaligem Versuch). Das generierte CAML ist aber voll funktionstüchtig.
Es sollten später aber sowieso die anzuzeigenden Felder im SharePoint Designer genauer definiert werden, sodass mein obiges Beispiel vollkommen problemlos funktioniert.

Viel Spaß beim LINQ padding!

Tagged , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: