Was eingegeben wird und dabei herauskommt
Die Vorlage wird als Stream gelesen:
string documentUrl = SPUrlUtility.CombineUrl
(web.Site.MakeFullUrl(templateLib.RootFolder.Url), templateName);
SPFile template = templateLib.RootFolder.Files[documentUrl];
Stream templateStream = template.OpenBinaryStream();
Wer sich im Umgang mit der SharePoint-Entwicklung nicht ganz sicher fühlt, sollte sich Klassen wie SPUrlUtility genauer ansehen. Sie helfen, die passenden Operationen elegant auszuführen. Das Objekt web liefert SPWeb, also die Anwendung, in der sich die Listen befinden. Der Code liest das betreffende Dokument aus der Dokumentenbibliothek und übergibt es als Stream. Wie gezeigt, ist das der Eingabetyp für die Packaging-Klasse. Die Ausgabe erfolgt über denselben Weg, nur wird der Stream hier geschrieben. Aus dem Paket liest der Entwickler die oben erwähnte Word-Datei document.xml wie folgt ein:
Uri uri = new Uri("/word/document.xml", UriKind.Relative);
PackagePart part = package.GetPart(uri);
Über einen Zwischenschritt (Listing 1) lädt er die Datei in ein XDocument-Objekt. In WordML bietet es sich an, den nötigen Namensraum und die Tag-Namen vorzuhalten, da das endlose Erzeugen von XName-Objekten den Code sonst unleserlich macht. Listing 2 zeigt, was für das Projekt nötig ist. Mit W.tagName lässt sich nun über nur zwei Anweisungen auf die Tabelle und die Platzhalter zugreifen. Tabellen basieren auf dem Tag <w:sdt>:
XElement tablePlaceHolder = doc.Descendants(W.sdt).FirstOrDefault(x =>
x.Element(W.sdtContent).Descendants(W.sdt).FirstOrDefault() != null);
Ihr Inhalt steht in <w:stdContent>, dieses Element wird im Aufruf zurückgegeben und daraus die Tabellenreihe extrahiert. Der Variablenname prototype dient als Vorlage für die wiederholte Ausgabe der Inhalte:
XElement prototype =
tablePlaceHolder.Element(W.sdtContent).Descendants(W.tr)
.Where(x => x.Descendants(W.sdt).FirstOrDefault() !=
null).FirstOrDefault();
Her mit den Daten
Im nächsten Schritt sind die Daten zu beschaffen. Dazu muss der Entwickler zunächst alle Elemente der SharePoint-Liste, im Beispiel mit dem Namen Finances, auslesen:
SPList dataList = web.Lists.Cast<SPList>().FirstOrDefault(list =>
list.RootFolder.Name == "Finances");
IEnumerable<SPListItem> allItems =
dataList.GetItems(new SPQuery()).Cast<SPListItem>();
Die LINQ-Abfrage nutzt ein LINQ-to-Object. Mit ihm wendet der Code hier eine LINQ-Abfrage auf einem IEnumerable<SPListItem> an, um die Listendaten komplett im Speicher zu verarbeiten. Bei großen Datenmengen sollte davor die passende CAML-Abfrage gesetzt werden, die sich in das genutzte SPQuery-Objekt platzieren lässt.
allItems dient jetzt als Datenquelle, und jedes Element produziert eine Reihe Daten auf Basis der Variablen prototype. Anschließend folgt eine komplexere LINQ-Anweisung, bei der die innere foreach-Schleife die Platzhalter-Elemente aus dem Prototyp in die Variable celltype holt. Die hat – per Definition – den Namen des Felds der Datenliste. Sicherheitshalber prüft der Programmierer den Namen mit ContainsField. Über li[celltag] kommt er an die Daten, die String.Format-Anweisung konvertiert sie in einen String und deutet zudem an, dass sich weitere Formatierungen einbauen lassen, um nachträglich komplexe Operationen zu ermöglichen. Auch eine Abfrage von Formatanweisungen aus der Quellliste wäre denkbar.
Da man für jede Datenreihe eine neue Tabellenreihe erstellen muss, ist noch der ursprüngliche Prototyp zu entfernen. Der fertige Bericht liegt jetzt bereits in XML vor. Über File.Add übergibt der Entwickler ihn an die Dokumentenbibliothek (die Quelle ist ein Stream). Anwender können sich den Bericht dann direkt aus der Bibliothek herunterladen.
Fazit
Eine Kombination aus SharePoint als Dokumentenverwaltungsapplikation, der SharePoint API und .NET 3.5 mit LINQ erlaubt eine codeseitig recht kompakte Umsetzung. Wie viel Kontrolle in der Vorlage ist und wie viel in der Datenquelle, hängt von der Anwendung und auch von den Benutzern ab. Prinzipiell ist es von Vorteil, die Gestaltung in Word sowie die Datenformatierung, Aufbereitung und Bereitstellung in der Applikation zu erledigen. Im Buch "SharePoint 2010 as a Development Platform" [1] gibt es dazu zahlreiche Anregungen.
Zwar bietet SharePoint 2010 Enterprise mit Word Services eine integrierte Funktion, um Word-Dokumente zu erzeugen, technisch ist das aber anspruchsvoll. Zudem ist die Enterprise-Lizenz recht teuer. Mit dem vorgestellten Beispiel, das sich auch in SharePoint 2010 und 2007 sowie Office 2010 und 2007 in jeder Kombination einsetzen lässt, kann man sich unnötigen Lernaufwand und Geld sparen. Und auf dem Server muss nicht einmal Office vorhanden sein, da der Entwickler direkt im XML arbeitet.
(ane)
Jörg Krause
arbeitet mit den Schwerpunktthemen Programmierung von Web- und Datenbankapplikationen mit .NET sowie SharePoint- und BizTalk-Programmierung als Senior Consultant bei Computacenter.
Julius Eder
ist Berater und Entwickler für IT-Prozessentwicklungen auf Basis von ASP.NET, SharePoint, Silverlight und SQL Server bei Computacenter.
Literatur
- Jörg Krause, Christian Langhirt, Alexander Sterff, Bernd Pehlke, Martin Döring; SharePoint 2010 as a Development Platform; apress 2010
Ab sofort kann man sich mit Vorträgen für die neue Konferenz zu Agile ALM, Continuous Delivery und DevOps bewerben.




