Gewusst wie: Ausführen von Code auf allen Webservern

Letzte Änderung: Montag, 19. April 2010

Gilt für: SharePoint Foundation 2010

Bei zahlreichen Entwicklungsszenarien, insbesondere solchen mit administrativen Funktionen, muss derselbe Code auf jedem Front-End-Webserver in der Farm ausgeführt werden. Beispielsweise müssen alle Front-End-Webserver in einer Microsoft SharePoint Foundation-Farm identisch konfiguriert und bereitgestellt werden, zumindest im Hinblick auf ihre Reaktion auf HTTP-Anforderungen. Eine Änderung an einer Einstellung in einer web.config-Datei muss also auf allen Front-End-Webservern nachvollzogen werden. Ein anderes Beispiel: Eine Assembly, die Teil einer Farmlösung ist, muss im globalen Assemblycache auf jedem Front-End-Webserver bereitgestellt werden.

Für viele Konfigurations- und Bereitstellungsaufgaben sind in SharePoint Foundation spezielle APIs enthalten, die dafür sorgen, dass die Server synchron bleiben. Beispielsweise stellt die SPWebConfigModification-Klasse Funktionen zum Ändern der Einstellungen im web.config-Dateistapel bereit. Weitere Informationen finden Sie unter Gewusst wie: Programmgesteuertes Hinzufügen und Entfernen von "Web.config"-Einstellungen. Wenn Sie eine Farmlösung bereitstellen, ob in der Anwendung für die Zentraladministration oder über die SharePoint-Verwaltungsshell, wird jede zur Lösung gehörende Assembly im globalen Assemblycache auf jedem der Front-End-Webserver bereitgestellt. Mithilfe der SPSolution.Deploy()-Methode können Sie benutzerdefinierte Bereitstellungsfunktionen für Farmlösungen erstellen.

Die speziellen APIs verwenden alle SharePoint Foundation-Zeitgeberaufträge, um ihre Funktion zu erfüllen. Der Name Zeitgeberauftrag kommt daher, dass für jeden Auftrag ein bestimmter Ausführungszeitpunkt in der Zukunft (oder unmittelbar nach der Erstellung) festgelegt werden kann. Ein anderer möglicher Name wäre Multiserverauftrag, weil festgelegt werden kann, dass der Auftrag auf mehreren Webservern, beispielsweise auf allen Front-End-Webservern, oder auf einem bestimmten Server ausgeführt wird. Wenn eine Funktion auf allen Front-End-Webservern ausgeführt werden soll und keine entsprechende Spezial-API vorhanden ist, können Sie die Zeitgeberauftrags-APIs verwenden. In diesem Thema wird erklärt, wie Sie dazu vorgehen.

HinweisHinweis

Die Unterscheidung zwischen einem Front-End-Webserver und einem Anwendungsserver ist in SharePoint Foundation 2010 weniger ausgeprägt als in früheren Versionen des Produkts. Bis auf eine Ausnahme wird die gesamte SharePoint Foundation-Software auf allen Servern installiert, unabhängig von deren Status als Anwendungsserver oder Front-End-Webserver. (Die Ausnahme betrifft den Server, auf dem die Microsoft SQL Server-Datenbank für die Farm gehostet wird. In der Regel wird SharePoint Foundation auf diesem Computer überhaupt nicht installiert. In diesem Thema bezieht sich der Ausdruck "alle Server" im Folgenden auf alle Server mit Ausnahme des dedizierten Datenbankservers, auf dem SharePoint Foundation nicht installiert wird.) Allgemein gilt, dass ein SharePoint Foundation-Server dann ein Front-End-Webserver ist, wenn der Microsoft SharePoint Foundation-Webanwendungsdienst auf ihm ausgeführt wird. Andernfalls handelt es sich um einen Anwendungsserver.

So definieren Sie einen Zeitgeberauftrag

  1. Starten Sie in Visual Studio ein leeresSharePoint-Projekt. Erstellen Sie eine Farmlösung, keine Lösung mit eingeschränkter Sicherheitsstufe.

  2. Markieren Sie im Projektmappen-Explorer den Projektnamen, und stellen Sie sicher, dass im Bereich Eigenschaften die Einstellung Assembly in Paket einschließen auf true festgelegt ist.

  3. Fügen Sie dem Projekt eine C#- oder Visual Basic-Klassendatei hinzu.

  4. Öffnen Sie die Klassendatei, und fügen Sie using-Anweisungen (Imports in Visual Basic) für die Namespaces Microsoft.SharePoint und Microsoft.SharePoint.Administration hinzu. Möglicherweise müssen Sie weitere using-Anweisungen hinzufügen, je nachdem, welche Namespaces von dem Code aufgerufen werden, der auf allen Servern ausgeführt werden soll. Für das Beispiel in diesem Thema fügen Sie using-Anweisungen für System.Xml.Linq, System.Xml.XPath, System.IO und System.Runtime.InteropServices hinzu.

  5. Ändern Sie den Namespace so, dass er den Richtlinien in Richtlinien für die Namespacebenennung entspricht, z. B. Contoso.SharePoint.Administration.

  6. Ändern Sie die Klassendeklaration, und geben Sie an, dass die Klasse von SPJobDefinition oder von einer beliebigen Klasse erbt, die von SPJobDefinition abgeleitet ist.

  7. Versehen Sie die Klassendeklaration mit einem GuidAttribute-Attribut. Dies ist für alle Klassen notwendig, die direkt oder indirekt von SPPersistedObject abgeleitet sind.

  8. Fügen Sie der Klasse einen (parameterlosen) Standardkonstruktor hinzu, der einfach den Basiskonstruktor aufruft.

  9. Fügen Sie einen Konstruktor mit Parametern vom Typ String, SPWebApplication, SPServer und SPJobLockType hinzu. Die Implementierung sollte den Basiskonstruktor SPJobDefinition(String, SPWebApplication, SPServer, SPJobLockType) aufrufen. Nachfolgend sehen Sie, wie der Code zum jetzigen Zeitpunkt in C# aussehen sollte.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;
    
    using System.Xml.Linq;
    using System.Xml.XPath;
    using System.IO; 
    using System.Runtime.InteropServices;
    
    namespace Contoso.SharePoint.Administration
    {
        [Guid("9573FAD9-ED89-45E8-BD8B-6A5034E03895")]
        public class MyTimerJob : SPJobDefinition
        {
            public MyTimerJob() : base() { }
    
            public MyTimerJob(String name, SPWebApplication wApp, SPServer server, SPJobLockType lockType)
                : base(name, wApp, server, lockType) { }
        }
    }
    
    HinweisHinweis

    Wenn ein Auftrag auf allen Servern einschließlich Anwendungsservern ausgeführt werden soll, sollte die Klasse von SPServiceJobDefinition abgeleitet werden. Übergeben Sie den Zeitgeberdienst (SPFarm.Local.TimerService) als SPService-Parameter des SPServiceJobDefinition(String, SPService)-Konstruktors.

  10. Fügen Sie Außerkraftsetzungen der Eigenschaften DisplayName und Description hinzu. Die DisplayName-Eigenschaft ist der benutzerfreundliche Name eines Auftrags, der in den Auftragsdefinitionen, geplanten Aufträgen und Auftragsverlaufslisten in der Anwendung für die Zentraladministration angezeigt wird. Description ist eine Beschreibung des Auftrags. Der Einfachheit halber werden diesen Elementen im folgenden Beispiel Literalzeichenfolgen zugewiesen. In der Praxis sollten stattdessen lokalisierte Zeichenfolgen zugewiesen werden.

    public override string DisplayName
    {
        get
        {
            // TODO: return a localized name
            return "Add Contoso Mobile Adapter";
        }
    }
    
    public override string Description
    {
        get
        {
            // TODO: return a localized description
            return "Adds a mobile adapter for Contoso's voting Web Part.";
        }
    }
    
  11. Fügen Sie der Klasse eine Außerkraftsetzung der Execute(Guid)-Methode hinzu.

    public override void Execute(Guid targetInstanceId)
    {
        // INSERT HERE CODE THAT SHOULD RUN ON ALL SERVERS.
    }
    
  12. Die Implementierung der Methode besteht ganz einfach in dem Code, der auf allen Front-End-Webservern ausgeführt werden soll. Dieser Code wird vom SharePoint 2010-Zeitgeberdienst aufgerufen, der auf allen SharePoint Foundation-Servern in der Farm ausgeführt werden sollte. Die Execute(Guid)-Methode wird vom Zeitgeberdienst aufgerufen, wenn der Auftrag ausgeführt wird, und wird im Benutzerkontext des Zeitgeberdiensts ausgeführt. Bei einer SharePoint Foundation-Installation mit einem einzigen Server ist dieser Benutzer in der Regel Netzwerkdienst. Interessanter sind Farmen mit mehreren Servern. In diesem Fall wird der Zeitgeberdienst im Kontext desselben Domänenbenutzerkontos ausgeführt, das die Farm zum Lesen und Schreiben der Inhalts- und Konfigurationsdatenbanken verwendet. Dieser Benutzer ist kein Computeradministrator auf einem Server, sondern Mitglied der Benutzergruppe WSS_ADMIN_WPG auf allen Servern. Er verfügt über die Rechte zum Lesen, Schreiben und Ausführen für die Ordner in der %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\-Struktur und in der c:\Inetpub\wwwroot\wss-Struktur.

    In diesem Thema gehen wir davon aus, dass Sie einen mobilen Webpartadapter zur Verwendung mit der mobilen Version von Webpartseiten erstellt haben. Sie müssen den Adapter in der Datei compat.browser für die Webanwendung registrieren. Diese Datei befindet sich im Ordner C:\Inetpub\wwwroot\wss\VirtualDirectories\portnumer\App_Browsers, wobei portnummer die Portnummer der Webanwendung ist, also beispielsweise 80. Auf jedem Front-End-Webserver gibt es eine eigene Kopie der Datei, die alle identisch bearbeitet werden müssen. Mit dem folgenden Code wird der Adapter in der Datei compat.browser auf jedem Server registriert, auf dem der Zeitgeberauftrag ausgeführt wird. (Weiter unten in diesem Thema erfahren Sie, wie Sie sicherstellen können, dass der Auftrag auf allen Servern ausgeführt wird.)

    TippTipp

    Zur Vereinfachung wird in diesem Beispiel in der Datei compat.browser nur die Standard-URL-Zone geändert. In der Praxis sollten alle Elemente von IisSettings mit einer Schleife durchlaufen werden.

    public override void Execute(Guid targetInstanceId)
    {
        // Set the path to the file. The code that creates the MyTimerJob object associates
        // the job with a Web application.
        String pathToCompatBrowser = this.WebApplication.IisSettings[SPUrlZone.Default].Path 
                                   + @"\App_Browsers\compat.browser";
    
        XElement compatBrowser = XElement.Load(pathToCompatBrowser);
    
        // Get the node for the default browser.
        XElement controlAdapters = compatBrowser.XPathSelectElement("./browser[@refID = \"default\"]/controlAdapters");
    
        // Create and add the markup.
        XElement newAdapter = new XElement("adapter");
        newAdapter.SetAttributeValue("controlType", 
            "Contoso.SharePoint.WebPartPages.VotingWebPart, Contoso, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ffec2e2af2b4c675");
        newAdapter.SetAttributeValue("adapterType",
            "Contoso.SharePoint.WebPartPages.VotingWebPartMobileAdapter, Contoso, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ffec2e2af2b4c675");
        controlAdapters.Add(newAdapter);
    
        // Overwrite the old version of compat.browser with your new version.
        compatBrowser.Save(pathToCompatBrowser);
    }
    
  13. Wählen Sie im Menü Build von Visual Studio die Option Lösung bereitstellen aus. Wenn die Eigenschaft Website-URL für Leeres SharePoint-Projekt in Visual Studio auf eine SharePoint Foundation-Farm mit einem einzigen Server zeigt, wird mit dieser Aktion die Assembly kompiliert, in einer SharePoint Foundation-Lösungsdatei (WSP-Datei) gepackt, das Paket in den Lösungskatalog der Farm hochgeladen und die Assembly im globalen Assemblycache bereitgestellt. Im Rahmen dieses Themas geht es jedoch um die Bereitstellung in einer Farm mit mehreren Servern. Sie können mit der Eigenschaft Website-URL auf eine solche Farm zeigen, allerdings wird in diesem Fall beim Klicken auf Lösung bereitstellen die Aktionskette beendet, sobald die Lösung in den Lösungskatalog der Farm hochgeladen wurde. Die Bereitstellung müssen Sie selbst durchführen. Eine Möglichkeit dazu haben Sie direkt im Katalog in der Anwendung für die Zentraladministration. Sie könnten aber auch die SharePoint-Verwaltungsshell verwenden. Eine weitere Option besteht darin, im Menü Build in Visual Studio auf Paket zu klicken. Mit dieser Aktion wird die Assembly kompiliert und gepackt. Anschließend können Sie das Paket im Lösungskatalog der Farm installieren und mithilfe der SharePoint-Verwaltungsshell bereitstellen. Unabhängig davon, welches Verfahren Sie nutzen, wird bei der Bereitstellung in einer Farm mit mehreren Servern die Assembly im globalen Assemblycache aller Front-End-Webserver installiert.

Erstellen einer Instanz eines Zeitgeberauftrags

Nachdem die Assembly mit der Definition des Zeitgeberauftragstyps auf allen Servern bereitgestellt wurde, können Sie programmgesteuert Instanzen der Assembly erstellen und planen. Der entsprechende Code kann in eine Vielzahl von Entwicklungskontexten eingebunden werden. Wenn der benutzerdefinierte Zeitgeberauftrag Teil einer Lösung ist, die Sie als SharePoint Foundation-Feature bereitstellen, könnten Sie den Auftragserstellungscode in eine Außerkraftsetzung der FeatureActivated(SPFeatureReceiverProperties)-Methode in einem Featureempfänger einschließen. (Als Bereich dieses Features muss die Farm oder die Webanwendung festgelegt werden.) Eine weitere Möglichkeit besteht darin, die Anwendung für die Zentraladministration mit einer benutzerdefinierten Aktion zu erweitern, die den Auftrag erstellt. Sie könnten auch ein benutzerdefiniertes PowerShell-Cmdlet erstellen, das in der SharePoint-Verwaltungsshell ausgeführt werden kann. In diesem Thema verwenden wir eine einfache Konsolenanwendung. Unabhängig davon, wo sich der Auftragserstellungscode befindet, muss er in jedem Fall im Benutzerkontext eines Farmadministrators ausgeführt werden.

Die wesentlichen Aufgaben des Codes sind die Konstruktion eines Objekts mit dem benutzerdefinierten Zeitgeberauftragstyp, dessen Verknüpfung mit einer Webanwendung und das Festlegen seiner Schedule-Eigenschaft.

So erstellen und planen Sie einen Zeitgeberauftrag

  1. Starten Sie in Visual Studio ein Konsolenanwendungsprojekt. Dieses kann ein zweites Projekt innerhalb derselben Visual Studio-Lösung wie das Zeitgeberauftragsprojekt oder eine vollkommen getrennte Visual Studio-Lösung sein. Beide Vorgehensweisen haben ihre Vor- und Nachteile. In diesem Thema setzen wir voraus, dass eine vollkommen getrennte Visual Studio-Lösung verwendet wird.

  2. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen, und wählen Sie Eigenschaften aus.

  3. Stellen Sie auf der Registerkarte Anwendung sicher, dass Zielframework auf .NET Framework 3.5 festgelegt ist.

  4. Auf der Registerkarte Build unter Zielplattform muss entweder x64 oder Beliebige CPU ausgewählt sein. Weitere Informationen zu dieser Entscheidung finden Sie unter Gewusst wie: Festlegen des richtigen Zielframeworks und der CPU.

  5. Klicken Sie im Menü auf die Schaltfläche zum Speichern allerDateien.

  6. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen, und wählen Sie Verweis hinzufügen aus. Fügen Sie auf der Registerkarte Projekte oder Durchsuchen einen Verweis auf die Assembly hinzu, die Sie mit dem oben beschriebenen Verfahren erstellt haben.

  7. Fügen Sie einen Verweis auf die Assemblys Microsoft.SharePoint und Microsoft.SharePoint.Security hinzu.

  8. Öffnen Sie die Codedatei, und fügen Sie using-Anweisungen (Imports in Visual Basic) für die Namespaces Microsoft.SharePoint und Microsoft.SharePoint.Administration hinzu.

  9. Ändern Sie entweder den Namespace so, dass er dem Namespace entspricht, den Sie im oben beschriebenen Verfahren für den benutzerdefinierten Zeitgeberauftrag verwendet haben, oder verwenden Sie einen anderen Namespace. In diesem Fall müssen Sie allerdings eine using-Anweisung für den Namespace hinzufügen, in der der benutzerdefinierte Zeitgeberauftrag deklariert wird.

  10. Fügen Sie der Main-Methode den folgenden Code hinzu:

    // Get a reference to the Web application for which you want to register the mobile Web Part adapter.
    SPWebApplication webApp = SPWebApplication.Lookup(new Uri("https://localhost/"));
    

    Dies ist die einzige Möglichkeit, einen Verweis auf eine Webanwendung zu erhalten. Weitere Informationen finden Sie unter Abrufen von Verweisen auf Websites, Webanwendungen und andere Schlüsselobjekte.

  11. Fügen Sie der Main-Methode den folgenden Code hinzu:

    // Create the timer job.
    MyTimerJob myTJ = new MyTimerJob("contoso-job-add-mobile-adapter", webApp, null, SPJobLockType.None);
    

    Beachten Sie folgende Aspekte in diesem Code:

    • Der erste Parameter des Konstruktors für MyTimerJob ist der interne Name des Auftrags. Es hat sich eingebürgert, dass interne Auftragsnamen in Kleinbuchstaben und mit Bindestrich geschrieben werden und mit dem Wort "job" beginnen. Sie können am Anfang des internen Auftragsnamens den Namen Ihres Unternehmens hinzufügen wie in diesem Beispiel.

    • Der zweite Parameter gibt an, auf welche Webanwendung der Auftrag angewendet werden soll.

    • Der dritte Parameter dient zur Angabe eines bestimmten Servers, auf dem der Auftrag ausgeführt werden soll. Der Parameter lautet null, wenn der Auftrag auf allen Front-End-Webservern ausgeführt werden soll.

    • Der vierte Parameter ermittelt, ob der Auftrag auf allen Front-End-Webservern ausgeführt wird. Durch Übergeben von SPJobLockType.None wird sichergestellt, dass er auf allen Servern ausgeführt wird, auf denen der Microsoft SharePoint Foundation-Webanwendungsdienst läuft. Durch Übergeben von SPJobLockType.Job hingegen wird sichergestellt, dass er nur auf dem ersten verfügbaren Server ausgeführt wird, auf dem der Microsoft SharePoint Foundation-Webanwendungsdienst läuft. (Es gibt noch einen dritten möglichen Wert. Weitere Informationen finden Sie unter SPJobDefinition und in den Themen zu den Konstruktoren und anderen Elementen.)

  12. Fügen Sie der Main-Methode den folgenden Code hinzu:

    // Schedule the job.
    myTJ.Schedule = new SPOneTimeSchedule(DateTime.Now.AddSeconds(30.0));
    

    SPOneTimeSchedule ist eine von mehreren Klassen, die von der SPSchedule-Klasse abgeleitet sind und als Wert der Schedule-Eigenschaft verwendet werden können. Sie können festlegen, dass der Auftrag sofort ausgeführt werden soll, indem Sie Now an den Konstruktor übergeben. Gerade in der Entwicklungsphase empfiehlt es sich jedoch, mit einer gewissen Verzögerung zu arbeiten, in diesem Fall 30 Sekunden, damit Sie genug Zeit haben, um zu sehen, ob der Auftrag in den Auftragsdefinitionen und in der Liste der geplanten Aufträge in der Anwendung für die Zentraladministration aufgeführt wird. Nach Abschluss wird er in der Auftragsverlaufsliste angezeigt. Ein wiederkehrender Auftrag bleibt auf unbestimmte Zeit in der Auftragsdefinitionsliste, während ein einmaliger Auftrag, also ein Auftrag, bei dem ein SPOneTimeSchedule-Objekt als Wert der Schedule-Eigenschaft dient, nur vom Zeitpunkt der Erstellung bis zur Ausführung (in diesem Fall also 30 Sekunden lang) existiert (und in der Auftragsdefinitionsliste aufgeführt wird). Nach der Ausführung wird er automatisch gelöscht.

  13. Fügen Sie der Main-Methode den folgenden Code hinzu:

    // Save the scheduled job instance to the configuration database.
    myTJ.Update();
    

Code wird kompiliert

  1. Erstellen Sie das Projekt in Visual Studio.

  2. Führen Sie die ausführbare Datei auf einem beliebigen Server in der Farm im Benutzerkontext eines Farmadministrators aus.

    Der Auftrag wird bis zum geplanten Ausführungszeitpunkt sowohl in der Auftragsdefinitionsliste als auch in der Liste geplanter Aufträge in der Anwendung für die Zentraladministration aufgeführt. Im vorliegenden Beispiel sind das 30 Sekunden. Zum geplanten Zeitpunkt wird er auf allen Servern ausgeführt. Anschließend wird er in der Auftragsverlaufsliste in der Anwendung für die Zentraladministration angezeigt und verschwindet aus der Liste der geplanten Aufträge. Da es sich um einen einmaligen Auftrag handelt, wird er nach erfolgreicher Ausführung auch nicht mehr in der Auftragsdefinitionsliste aufgeführt.

  3. Überprüfen Sie, dass der Auftrag auf allen Servern ausgeführt wurde. Im vorliegenden Beispiel überprüfen Sie hierzu, ob das controlAdapter-Element mit seinen zwei Attributen der Datei compat.browser im Ordner C:\Inetpub\wwwroot\wss\VirtualDirectories\portnummer\App_Browsers hinzugefügt wurde, wobei portnummer der Portnummer der Webanwendung auf den einzelnen Servern entspricht.