Main /

Tipps zur Entwicklung auf dem Sharepoint Objektmodell

Tipps zur Entwicklung auf dem Sharepoint Objektmodell

Tipps, Tricks und Erfahrungen mit dem Sharepoint Objekt Modell (Sharepoint 2003-2013)

Gesammelte Tipps und Kniffe, die ich bei der Entwicklung unter Windows Sharepoint Services (WSS) und Sharepoint Portal Server selbst zusammengetragen habe. Es handelt sich hier also um Lösungen für real auftretende Probleme.

Information zur Erstellung und zum Deployment von Webparts finden sich auf dieser Seite: WebPartEntwicklung

1. Impersonierung (SharePoint 2007 - 2013)

Code mit den Richten des System-Users ausführen

Guid webid = SPContext.Current.Web.ID;
Guid siteid = web.Site.ID;
SPSecurity.RunWithElevatedPrivileges(() =>
{
    using (SPSite site = new SPSite(siteid))
    {
        using (SPWeb web = site.OpenWeb(webid))
        {
           // Mach sinnvolle Sachen im Kontext des System-users
        }
    }
});

2. Wie lösche ich ein Listen-Item?

Angenommen das Listen-Objekt (vom Typ SPList) befindet sich in der Variable list.

Wenn die Item-ID bekannt ist:

if (list.AllowDeletion) {
   list.Items.DeleteItemById(id);
   list.Update();
}

oder wenn der Index bekannt ist:

if (list.AllowDeletion) {
   list.Items.Delete(index);
   list.Update();
}

Wenn der aktuelle User keine Schreibrechte auf die Liste hat, kann er auch kein Item löschen. In dem Fall hilft Impersonieren (siehe oben).

Hinweis: Vorher muß in jedem Fall die Eigenschaft AllowUnsafeUpdates des SPWeb zu dem die Liste gehört auf true gesetzt werden!

3. Wie bekomme ich alle Top-Level WSS-Sites? (SP 2003)

Die Sitestruktur in Sharepoint ist leider kein echter Baum, d.h. es können mehrere Top-Level-Sites (Rootknoten) existieren. Würde man sich einfach mit SPWeb.ParentWeb schrittweise nach oben hangeln, wäre man irgendwann dort angekommen, ParentWeb ist null und man hat nur das Rootweb, das den Knoten für den Teilbum darstellt, in dem man sich bewegt hat. Um das Problem zu lösen muß man das GlobalAdmin-Objekt bemühen, wofür man wiederum Administratorretche braucht und impersonieren muss (siehe oben):

// Alle Top-Level-Sites ermitteln
SPGlobalAdmin globalAdmin = new SPGlobalAdmin();
SPSiteCollection sites = null;

try {
   // bestimme den eigenen virtuellen Server
   sites = globalAdmin.OpenVirtualServer(Page.Request.Url).Sites;
   // hack: test ob sich die erste Site ansprechen lässt
   SPSite testsite = sites[0];
}
catch (Exception ex) {
   throw (
     new Exception("Kann virtuellen Server nicht öffnen!<br>"
                   + ex.Message.ToString())
   );
}

// Alle root-Webs durchgehen
foreach (SPSite site in sites) {
   SPWeb web= site.RootWeb;
   // so, jetzt fehlt hier nur noch sinnvoller code. ;-)
}

4. Wie kann ich dynamisch befüllte Drop-Down Menüs in den Web-Part-properties darstellen?

Bevor es Text gibt, gibt es einen Link zum Thema:

http://www.mysharepoint.de/KnowHow/tabid/108/ArticleType/ArticleView/ArticleID/28/Default.aspx

5. Ein eigenes Webpart, das mit Listen arbeitet ist extrem langsam

Das Sharepoint Objekt-Modell birgt einige Tücken. So ist z.B. der Aufruf von SPList.Items extrem langsam und sollte nicht in einer Schleife ausgeführt werden.

Anstatt

for (int i = 0; i < mylist.Items.Count; i++) {
   SPListItem item = mylist.Items[i];
   // [[..]]
}

schreibt man also besser:

SPListItemCollection listitems = mylist.Items;
for (int i = 0; i < listitems.Count; i++) {
   SPListItem item = listitems.Items[i];
   // [[..]]
}

Das gleiche gilt natürlich auch für foreach-Schleifen.

Achtung: Ab Sharepoint 2010 muss man den List-Threshold beachten und den Content-Iterator verwenden Mehr dazu:

6. Schnelles Abfragen einer Liste mit CAML-Queries

Wenn man bestimmte Items aus einer Liste heraussuchen möchte, kann man besser ein CAML-Query verwenden, als die Items sequentiell herauszusuchen. Ein nettes Tool, um CAML-Query zu testen ist der CAML-Query-Builder - unbedingt empfehlenswert.

Hinweis: Der CAML-Builder schließt die komplette Abfrage in <Query></Query>-Tags ein. Die müssen bei Verwendung mit der SPQuery-Klasse weggelassen werden, sonst funktioniert die Query nicht richtig!

Ein CAML-Query, das z.B. alle List-Items herraussucht, bei denen im Feld "Benutzer" "Adminstrator" steht und das Flag "Archiv" gesezt ist lautet z.B. so:

<Where>
  <And>
    <Eq>
      <FieldRef Name="Benutzer" />
      <Value Type="User">Administrator</Value>
    </Eq>
    <Eq>
      <FieldRef Name="Archiv" />
      <Value Type="Boolean">true</Value>
    </Eq>
  </And>
</Where>

Und so wird der Query abgesetzt, man erhält als Resultat eine SPListItemCollection die man dann in einer Schleife sequentiell durchlaufen kann, da sie nur die Items enthält die man haben will. In unserem Beispiel heißt die Liste myList

string caml = "hier CAML-Query String einfügen";
SPQuery listquery = new SPQuery();
listquery.Query = caml;
SPListItemCollection listitems = myList.GetItems(wkquery);

Leider ist es nicht möglich, über CAML nach den IDs? eines Lookup-Feldes zu filtern. Entweder CAML prüft auf den geasamten Eintrag eines Lookup-Feldes (siehe Punkt 7), oder nur auf den Text-Teil. Das ist leider nicht hilfreich.

7. Wie ist ein Nachschlagen-Feld aufgebaut?

Mit dem Feldtyp Nachschlagen (engl. lookup) lassen sich Sharepoint-Listen auf User-Ebene miteinander verknüpfen, um aus den Einträgen einer anderen Liste einen auszuwählen.

Nun könnte man denken dass es sich bei einem Lookup-Feld intern um ein Integer oder GUID-Feld handelt, dass einfach die ID oder GUID des referenzierten Datensdatzes aus der anderen Liste beinhaltet. Nicht so bei Sharepoint. Hier haben die Lookup-Felder einen eigenen Datentyp, bzw. sind vom Typ String - ein Textfeld also. Schaut man sich den Inhalt eines solchen Lookup-feldes an, findet man folgendes:

6;#Frikadelle

Die 6 ist die Datensatz-ID der referenzierten Liste, und Frikadelle ist der Inhalt genau des Feldes das bereits referenziert wird. Doppelt gemoppelt mit eingebauten Redundanzen.

Hier ist eine kleine Routine aus meiner Toolbox, um ein Lookup-Feld zu zerpflücken. Nix dolles, aber praktisch:

// --------------------------------------------------------------------------
/// <summary>
/// Splittet den Inhalt eines Listen-Lookup-Feldes
/// </summary>
/// <param name="lookup">Inhalt des Lookup-Files</param>
/// <returns>String-Array: [0] = ID,  [1] = Name</returns>
public static string[] SplitLookup (string lookup) {
   string[] result = new string[] {"",""};
   if (lookup != null) {
      if (lookup.IndexOf(';') > 0) {
         result[0] = lookup.Substring(0,lookup.IndexOf(';'));
         result[1] = lookup.Substring(lookup.IndexOf(';')+2);
      }
      else {
         result[0]=lookup;
      }
   }
   return result;
}

8. Wie bekomme ich einen PortalContext auf einer WSS-Seite? (SPS 2003)

Manchmal benötigt man auf einer Sharepoint-Services Seite einen Portal-Kontext, z.B. um Benutzer-Profile zu laden und auszuwerten, die nur im Portal-Server zur verfügung stehen. Die "normale" Methode, sich ein PortalContext-Objekt mit Hilfe eines HttpContext zu erzeugen funktioniert nur auf Portalseiten. Hier ein nacktes Beispiel ohne jegliche Fehlerbehandlung wie man sich das Profil des aktuellen Users holt.

string localPath = @"http://" + Environment.MachineName;
Uri uri = new Uri(localPath);
TopologyManager topMan = new TopologyManager();
PortalSiteCollection sites = topMan.PortalSites;
PortalContext ctx = PortalApplication.GetContext(sites[uri]);
UserProfile user = upm.GetUserProfile(SPControl.GetContextWeb(Context).CurrentUser.LoginName);

Siehe auch hier.

Siehe auch

Links

Frische Änderungen | Menü editieren
zuletzt geändert am 21.03.2014 09:06 Uhr von Elsni
Edit Page | Page History