Category Archives: SharePoint

PowerPivot OData Feeds filtern

Wenn jemand PivotTabellen erstellen möchte, welche sich über mehrere unabhängige Datenquellen verteilen ist PowerPivot ein geniales Werkzeug.

SharePoint kann über die REST-Schnittstelle eingebunden werden, welches das OData Protokoll implementiert. Dazu muss lediglich die WCF Data Services Bibliothek – die Implementierung von OData über WCF – installiert werden. Diese Schnittstelle ist über http://<sharepoint_server>/_vti_bin/listdata.svc erreichbar.

Eigentlich dachte ich, dass dieses Protokoll voll und ganz von PowerPivot unterstützt wird (zumindest wird einem das immer so erklärt). Leider gibt es einen Bereich, welcher nicht unterstützt wird:
Das Vorfiltern der Daten über den QueryString ist leider nicht möglich. Dies ist dann nützlich, wenn man vermeiden möchte, dass die komplette Liste ausgelesen wird (und anschließend in der Excel-Tabelle gespeichert wird). Die Excel Datei kann so gut und gerne 20 bis 50 MB und größer werden, je nach Anzahl an Elementen.

OData bietet für das Filtern, Sortieren und Selektieren QueryString-Parameter an.
Um z.B. alle Buchungen aus dem Jahr 2012 zu filtern kann folgendes geschrieben werden:
…/listdata.svc/Buchungen?$filter=year(Erhalten) eq 2012

Leider mag PowerPivot QueryStrings ganz und gar nicht! Und mit ganz und gar nicht meine ich, dass diese einfach ignoriert werden. Dass ich nicht der Einzige mit diesem Problem ist sieht man an folgendem Bug Report: https://connect.microsoft.com/SQLServer/feedback/details/724823/import-odata-feed-datasource-into-powerpivot-without-filter

Da ich aber nicht so lange warten wollte, bis dieses Feature in PowerPivot Einzug findet habe ich eine Quick & Dirty Lösung zusammengebastelt, welche funktioniert, aber nicht wirklich schön ist.

Eigentlich sind mir zwei Lösungen eingefallen, die Eine greift in die IIS-Konfiguration von SharePoint ein (der geneigte Leser soll selbst entscheiden, ob dies eine gute Lösung ist), die Andere dient als Gateway zum OData-Feed von SharePoint.
Die zweite Lösung ist etwas komplexer – nicht sehr viel komplexer – aber genug, um aus dem Artikel zwei zu machen. Ich habe zurzeit nicht vor auf diese Lösung genauer einzugehen, es sei den ein Leser ist an der zweiten Lösung interessiert, dann werde ich einen weiteren Blog-Eintrag posten.

Quick & Dirty Lösung: Eingriff in die IIS-Konfiguration von SharePoint

Okay, das Ändern der IIS-Konfiguration von SharePoint ist eigentlich ein absolutes NoGo (außer in den von der SharePoint Dokumentation ausdrücklich erlaubten Fällen), die Lösung hat sich aber als recht einfach und wirkungsvoll erwiesen.

Ich bediene mich hier einfach dem URL Rewriting Modul, welches einem im IIS ab Version 7 zur Verfügung steht (muss aber nachinstalliert werden: http://www.iis.net/download/URLRewrite). Eine “User-friendly URL” Rule später funktioniert das Ganze auch schon. Zu beachten ist allerdings, dass der Webserver-Prozess recycelt wird (nicht der App-Pool, geht also etwas schneller), nachdem Änderungen am URL-Rewriting vorgenommen wurden, das Ganze ist in Produktion also mit Vorsicht zu genießen.

Die URL-Regel in meinem Fall sieht beispielsweise so aus:
image

Im Grunde wird nichts Anderes gemacht, als dass die URL
http://portal/projects/buchungsportal/_vti_bin/listdata/BuchungenKundeImJahr/2012
über einen URL Match
^projects/buchungsportal/_vti_bin/listdata/BuchungenKundeImJahr/([^/]+)/?$
umgeleitet wird zu
projects/buchungsportal/_vti_bin/listdata.svc/Buchungen?$filter=InhaltstypID%20eq%20’0x010100F72541062D58004C81F84CFC226B954702007BDDD584C443764C8A87B2F22748C1AF’%20and%20year(Erhalten)%20eq%20{R:1}

Das Jahr ist also variabel und wird in {R:1} eingefügt, der Inhaltstyp ist immer derselbe, muss also nicht über die URL bestimmt werden (könnte aber ohne Weiteres gemacht werden).

Ein rascher Test mit PowerPivot beweist, dass das Rewriting funktioniert:
image

image

Es kommen auch wirklich nur die Buchungen zurück, welche nach der InhaltstypID und dem Jahr gefiltert wurden.

Tagged

SharePoint 2010 Management Shell Ausgaben anpassen

Die “SharePoint 2010 Management Shell”, welches im Grunde eine PowerShell-Instanz ist, welche das PSSnapin mit dem Namen Microsoft.SharePoint.PowerShell geladen hat, ist extrem flexibel.

Ich könnte jetzt eine seitenweise Huldigung über die PowerShell schreiben, werde mich aber am Riemen reißen und nur einen der vielen Erweiterungspunkte der PowerShell ansprechen.

Die Erweiterungspunkte welche ich ansprechen möchte sind in der Hilfe von PowerShell dokumentiert.
Die beiden man-pages (wenn man die Linux-kompatible Bezeichnung für die Hilfe verwendet) nennen sich about_Types.ps1xml und about_Format.ps1xml:

man about_Types.ps1xml
man about_Format.ps1xml

Was mit diesen beiden XML-Dateien möglich ist, ist zum Einen das Typsystem der PowerShell zu erweitern (Types.ps1xml) und zum Anderen die Ausgabe auf dem Host zu beeinflussen (Format.ps1xml).

Types.ps1xml

Eines der wohl promintesten Beispiele für eine Erweiterung des Typsystems ist wohl das Count AliasProperty des Array-Objektes. .NET-Programmierer wissen, dass die Länge eines Arrays über das Length-Property ermittelt wird. Andere Collections benutzen dafür aber das Count-Property. Um diese Inkonsistenz zu vermeiden führt die PowerShell das Length AliasProperty ein.

Überall in der PowerShell findet man solche Typerweiterungen. Es gib verschiedenste Möglichkeiten einen Typ zu erweitern, AliasProperty ist die simpelste Möglichkeit und ist einfach ein Verweis auf ein bereits bestehendes Property (im Beispiel des Array.Count ist dies ein Verweis auf Array.Length). Es gibt aber auch komplexere, mit welchen Berechnungen möglich sind, wie z.B. das ScriptProperty. Weitere Informationen bitte about_Types.ps1xml entnehmen.

Format.ps1xml

Diese Datei ermöglicht die Ausgabe der Powershell auf dem Host zu beeinflussen. Ein einfaches Beispiel ist die Ausgabe des Verzeichnissinhaltes mit dir: da es sich im Grunde um .NET-Objekte handelt, welche zurückgegeben werden, muss der PowerShell mitgeteilt werden, wie diese schlussendlich dem Benutzer präsentiert werden sollen, da sonst jede einzelne Eigenschaft in einer Liste ausgegeben wird. Lesbarer (und auch vom DOS-Prompt her gewohnt) ist die Ausgabe mit einer eingeschränkten Anzahl von Eigenschaften und einer tabellarischen Ausgabe. Und genau hierfür sorgt die angesprochene Datei. Weitere Informationen bitte about_Format.ps1xmlentnehmen.

Und was bringt’s mir im Zusammenhang mit SharePoint?

Nun, SharePoint bietet leider eine sehr rudimentäre Implementierung dieser beiden Erweiterungspunkte. Zu finden sind die Formatdateien unter: %PROGRAMFILES%Common FilesMicrosoft SharedWeb Server Extensions14CONFIGPOWERSHELLFormat und die Typedateien unter: %PROGRAMFILES%Common FilesMicrosoft SharedWeb Server Extensions14CONFIGPOWERSHELLTypes.

Wie gesagt, diese Format- und Typdefinitionen sind recht rudimentär. Auf einem Produktivsystem sollten diese Definitionen aber ausreichen. Auf einem Entwicklersystem ist es aber manchmal bequemer, wenn man etwas mehr Informationen aus der PowerShell-Ausgabe erhält, als dies standardmäßig der Fall ist.

Weiter unten habe ich zwei Dateien angehängt, welche nach belieben angepasst werden können. Sie sollen lediglich als Ideengeber dienen.

Geladen werden die Dateien mittels den zwei Kommandos:

Update-TypeData -Prepend SharePoint.types.ps1xml
Update-FormatData -Prepend SharePoint.format.ps1xml

Diese beiden Befehle können im Profil hinterlegt werden, sodass sie beim Aufruf der PowerShell automatisch geladen werden.

Die Reihenfolge sollte wie oben sein (Type, dann Format), da die Ausgabe oftmals auf die definierten Typen verweist.

Falls nachträglich Änderungen an den beiden Dateien durchgeführt wurden, können die Definitionen über Update-TypeData und Update-FormatData neu geladen werden (ohne -Prepend).

Beispielausgabe

Standardausgabe von SharePoint:

Nach dem Laden der neuen Typ- und Ausgabedefinitionen:

Beispieldateien

Die Informationen, welche ausgegeben werden sollen, können für jeden Benutzer/Entwickler unterschiedlich sein, deshalb habe ich jeweils ein Beispiel-XML für die Formate und eines für die Types als Ausgangsdateien erstellt. Diese können dann selbst erweitert und angepasst werden:

SharePoint.format.ps1xml

<Configuration>
  <ViewDefinitions>
    <View>
      <Name>Microsoft.SharePoint.SPWeb</Name>
      <ViewSelectedBy>
        <TypeName>Microsoft.SharePoint.SPWeb</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <TableHeaders>
          <TableColumnHeader>
            <Width>25</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>20</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>9</Width>
          </TableColumnHeader>
          <TableColumnHeader />
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>Title</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>LastItemModifiedDate</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Language2</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Url</PropertyName>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
    <View>
      <Name>Microsoft.SharePoint.SPList</Name>
      <ViewSelectedBy>
        <TypeName>Microsoft.SharePoint.SPList</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <TableHeaders>
          <TableColumnHeader />
          <TableColumnHeader>
            <Width>20</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>10</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>9</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>36</Width>
          </TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>Title</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>LastItemModifiedDate</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>FieldCount</PropertyName>
                <Alignment>Right</Alignment>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>ItemCount</PropertyName>
                <Alignment>Right</Alignment>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>ID</PropertyName>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
    <View>
      <Name>Microsoft.SharePoint.SPListItem</Name>
      <ViewSelectedBy>
        <TypeName>Microsoft.SharePoint.SPListItem</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <TableHeaders>
          <TableColumnHeader>
            <Width>8</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>19</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>20</Width>
            <Label>ContentType</Label>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>7</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>20</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>19</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>19</Width>
          </TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>ID</PropertyName>
                <Alignment>Right</Alignment>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Title</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <ScriptBlock>$_.ContentType.Name</ScriptBlock>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Version</PropertyName>
                <Alignment>Right</Alignment>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Author</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Created</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Modified</PropertyName>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
  </ViewDefinitions>
</Configuration>

SharePoint.types.ps1xml

<Types>
  <Type>
    <Name>Microsoft.SharePoint.SPWeb</Name>
    <Members>
      <AliasProperty>
        <Name>LCID</Name>
        <ReferencedMemberName>Language</ReferencedMemberName>
        <TypeName>System.Int32</TypeName>
      </AliasProperty>
      <ScriptProperty>
        <Name>Language2</Name>
        <GetScriptBlock>
          [System.Globalization.CultureInfo]::GetCultureInfo($this.LCID).Name
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
  <Type>
    <Name>Microsoft.SharePoint.SPList</Name>
    <Members>
      <AliasProperty>
        <Name>Url</Name>
        <ReferencedMemberName>DefaultViewUrl</ReferencedMemberName>
        <TypeName>System.Uri</TypeName>
      </AliasProperty>
      <ScriptProperty>
        <Name>FieldCount</Name>
        <GetScriptBlock>
          $this.Fields.Count
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
  <Type>
    <Name>Microsoft.SharePoint.SPListItem</Name>
    <Members>
      <ScriptProperty>
        <Name>Url</Name>
        <GetScriptBlock>
          $this["ServerUrl"]
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Author</Name>
        <GetScriptBlock>
          $this.Fields.GetFieldByInternalName("Author").GetFieldValueAsText($this["Author"])
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Created</Name>
        <GetScriptBlock>
          [DateTime]$this["Created"]
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Modified</Name>
        <GetScriptBlock>
          [DateTime]$this["Modified"]
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Version</Name>
        <GetScriptBlock>
          if ($this.Versions -and $this.Versions.Count -gt 0) {
          $this.Versions[0].VersionLabel
          }
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>FieldCount</Name>
        <GetScriptBlock>
          $this.Fields.Count
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>WorkflowCount</Name>
        <GetScriptBlock>
          if ($this.Workflows) { $this.Workflows.Count } else { 0 }
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>RunningWorkflowCount</Name>
        <GetScriptBlock>
          if ($this.Workflows -and $this.Workflows.Count -gt 0) {
          @($this.Workflows | ? { !$_.IsCompleted }).Count
          } else { 0 }
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
</Types>
Tagged , ,

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 , , ,