Partager via


Infrastructure de travail du minuteur PnP

Le cours Infrastructure de travail du minuteur PnP est conçu pour simplifier la création de processus d’arrière-plan qui fonctionnent sur les sites SharePoint. L’infrastructure du travail du minuteur est similaire aux travaux du minuteur de code de confiance totale locaux (SPJobDefinition). La principale différence entre l’infrastructure du travail du minuteur et le travail du minuteur de code de confiance totale est que l’infrastructure de travail du minuteur utilise uniquement des API côté client et peut donc (et doit) être exécutée en dehors de SharePoint. L’infrastructure de travail du minuteur permet de créer des travaux de minuteur qui fonctionnent sur SharePoint Online.

Une fois qu’un travail du minuteur a été créé, il doit être planifié et exécuté. Les deux options les plus courantes sont les suivantes :

  • Lorsque Microsoft Azure est la plateforme d’hébergement, les travaux du minuteur peuvent être déployés et exécutés en tant que Tâches web Azure.
  • Lorsque Windows Server est la plateforme d’hébergement (par exemple, pour SharePoint local), les travaux du minuteur peuvent être déployés et exécutés dans le planificateur Windows.

Pour une présentation vidéo des travaux du minuteur, consultez la vidéo Présentation de l’infrastructure du travail du minuteur PnP, qui présente l’infrastructure du travail du minuteur et illustre l’exemple de travail du minuteur simple.

Exemple de travail du minuteur simple

Dans cette section, vous allez apprendre à créer un travail de minuteur très simple. L’objectif de cet exemple est de fournir au lecteur une vue rapide ; plus loin, nous fournissons une explication plus détaillée de l’infrastructure du travail du minuteur.

Remarque

Pour obtenir une solution PnP plus complète avec dix exemples de travaux de minuteur individuels, des exemples « Hello world » aux travaux d’expiration de contenu réels, consultez Core.TimerJobs.Samples.

Les étapes suivantes décrivent comment créer un travail de minuteur simple.

Étape 1 : Créer un projet console et référencer PnP Core

Créez un projet de type « console » et référencez la bibliothèque PnP Core en effectuant l’une des opérations suivantes :

  • Ajoutez le package NuGet Office 365 Developer Patterns and Practices Core à votre projet. Il existe un package NuGet pour v15 (local) et pour v16 (Office 365). Il s’agit de l’option recommandée.

  • Ajoutez le projet source PnP Core existant à votre projet. Cela vous permet d’effectuer un pas à pas détaillé dans le code principal PnP lors du débogage.

    Remarque

    Vous serez responsable de la mise à jour de ce code avec les dernières modifications ajoutées à PnP.

Étape 2 : Créer une classe de travail de minuteur et ajouter votre logique de travail de minuteur

  1. Ajoutez une classe pour le travail du minuteur nommée SimpleJob.

  2. La classe hérite de la classe de base abstraite TimerJob .

  3. Dans le constructeur, donnez un nom au travail du minuteur (base("SimpleJob")) et connectez le gestionnaire d’événements TimerJobRun .

  4. Ajoutez la logique de votre travail de minuteur au gestionnaire d’événements TimerJobRun .

Le résultat sera semblable à ce qui suit :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SharePoint.Client;
using OfficeDevPnP.Core.Framework.TimerJobs;

namespace Core.TimerJobs.Samples.SimpleJob
{
    public class SimpleJob: TimerJob
    {
        public SimpleJob() : base("SimpleJob")
        {
            TimerJobRun += SimpleJob_TimerJobRun;
        }

        void SimpleJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            e.WebClientContext.Load(e.WebClientContext.Web, p => p.Title);
            e.WebClientContext.ExecuteQueryRetry();
            Console.WriteLine("Site {0} has title {1}", e.Url, e.WebClientContext.Web.Title);
        }
    }
}

Étape 3 : Mettre à jour Program.cs pour utiliser le travail du minuteur

Le travail du minuteur créé à l’étape précédente doit toujours être exécuté. Pour ce faire, mettez à jour Program.cs en procédant comme suit :

  1. Instanciez votre classe de travail du minuteur.

  2. Fournissez les détails d’authentification pour le travail du minuteur. Cet exemple utilise le nom d’utilisateur et le mot de passe pour s’authentifier auprès de SharePoint Online.

  3. Ajoutez un ou plusieurs sites auxquels accéder le programme de travail du minuteur. Cet exemple utilise un caractère carte générique dans l’URL. Le travail du minuteur s’exécute sur tous les sites qui correspondent à cette URL de carte générique.

  4. Démarrez le travail du minuteur en appelant la méthode Run .

static void Main(string[] args)
{
    // Instantiate the timer job class
    SimpleJob simpleJob = new SimpleJob();

    // The provided credentials need access to the site collections you want to use
    simpleJob.UseOffice365Authentication("user@tenant.onmicrosoft.com", "pwd");

    // Add one or more sites to operate on
    simpleJob.AddSite("https://<tenant>.sharepoint.com/sites/d*");

    // Run the job
    simpleJob.Run();
}

Options de déploiement du travail du minuteur

L’étape précédente illustre un travail de minuteur simple. L’étape suivante consiste à déployer le travail du minuteur.

Un travail du minuteur est un fichier .exe qui doit être planifié sur une plateforme d’hébergement. Selon la plateforme d’hébergement choisie, le déploiement diffère. Les sections suivantes décrivent les deux options de plateforme d’hébergement les plus courantes :

  • Utilisation d’Azure comme plateforme d’hébergement.
  • Utilisation de Windows Server comme plateforme d’hébergement.

Déployer des travaux de minuteur sur Azure à l’aide d’Azure WebJobs

Avant de déployer un travail de minuteur, assurez-vous que le travail peut s’exécuter sans interaction de l’utilisateur. L’exemple de cet article invite l’utilisateur à fournir un mot de passe ou une clé secrète client (voir plus dans la section Authentification ), qui fonctionne lors du test, mais ne fonctionne pas lors du déploiement. Les exemples existants permettent tous à l’utilisateur de fournir un mot de passe ou une clé secrète client à l’aide du fichier app.config :

  <appSettings>
    <add key="user" value="user@tenant.onmicrosoft.com"/>
    <add key="password" value="your password goes here!"/>
    <add key="domain" value="Contoso"/>
    <add key="clientid" value="a4cdf20c-3385-4664-8302-5eab57ee6f14"/>
    <add key="clientsecret" value="your clientsecret goes here!"/>
  </appSettings>

Une fois ces modifications ajoutées au fichier app.config, exécutez le travail du minuteur à partir de Visual Studio pour vérifier qu’il s’exécute sans intervention de l’utilisateur.

Le déploiement réel sur Azure est basé sur Azure WebJobs. Pour déployer cet exemple de travail du minuteur, procédez comme suit :

  1. Cliquez avec le bouton droit sur le projet dans Visual Studio et choisissez Publier en tant que Tâche web Azure.

  2. Fournissez une planification pour le travail du minuteur, puis choisissez OK.

  3. Choisissez Sites web Microsoft Azure comme cible de publication. Vous serez invité à vous connecter à Azure et à sélectionner le site web Azure qui hébergera le travail du minuteur (vous pouvez également en créer un nouveau si nécessaire).

  4. Choisissez Publier pour envoyer (push) la tâche web vers Azure.

  5. Une fois le travail du minuteur publié, vous pouvez déclencher le travail et case activée l’exécution du travail à partir de Visual Studio ou du Portail Azure.

    Portail Azure (hérité)

  6. En outre, le travail du minuteur peut être exécuté à partir de la nouvelle Portail Azure en sélectionnant le travail et en choisissant Exécuter. Pour plus d’informations sur l’utilisation de WebJobs à partir du nouveau portail, consultez l’article Exécuter des tâches en arrière-plan avec webJobs dans Azure App Service.

    Portail Azure (actuel)

Remarque

Pour obtenir des conseils détaillés sur le déploiement d’une tâche web Azure, consultez Prise en main d’Azure WebJobs (travaux du minuteur) pour vos sites Office 365.

Déployer des travaux du minuteur sur Windows Server à l’aide du planificateur Windows

Lorsqu’il est déployé sur Windows Server, le travail du minuteur doit s’exécuter sans interaction de l’utilisateur.

  1. Modifiez le fichier app.config comme décrit dans la section précédente Déployer des travaux de minuteur sur Azure à l’aide d’Azure WebJobs.

  2. Copiez la version de mise en production de votre travail sur le serveur sur lequel vous souhaitez qu’elle s’exécute.

    Importante

    Copiez tous les assemblys appropriés, le fichier .exe et le fichier .config pour vous assurer que le travail peut s’exécuter sur le serveur sans installer de fichiers ou de programmes supplémentaires sur le serveur.

  3. Planifiez l’exécution du travail du minuteur. Nous vous recommandons d’utiliser le planificateur de tâches Windows intégré. Pour utiliser le Planificateur de tâches Windows :

    1. Ouvrez le Planificateur de tâches (Panneau de configuration>Scheduler de tâches).
    2. Choisissez Créer une tâche et spécifiez un nom et un compte qui exécutera la tâche.
    3. Choisissez Déclencheurs et ajoutez un nouveau déclencheur. Spécifiez la planification souhaitée pour le travail du minuteur.
    4. Choisissez Actions et choisissez l’action Démarrer un programme, sélectionnez le fichier de travail du minuteur .exe, puis définissez le fichier démarrer dans le dossier.
    5. Choisissez OK pour enregistrer la tâche.

Planificateur de tâches Windows

Infrastructure du travail du minuteur en profondeur

Cette section détaille les fonctionnalités de l’infrastructure de travail du minuteur et leur fonctionnement.

Structure

La classe TimerJob est une classe de base abstraite qui contient les propriétés publiques, méthodes et événements suivants :

Structure de classe TimerJob

La plupart des propriétés et méthodes sont expliquées plus en détail dans les sections à venir. Les autres propriétés et méthodes sont décrites ici :

  • Propriété IsRunning : obtient une valeur indiquant si le travail du minuteur est en cours d’exécution. Valeur true en cas d’exécution ; false s’il n’est pas en cours d’exécution.
  • Propriété Name : obtient le nom du travail du minuteur. Le nom est initialement défini dans le constructeur de travail du minuteur.
  • Propriété SharePointVersion : obtient ou définit la version de SharePoint. Cette propriété est automatiquement définie en fonction de la version du Microsoft.SharePoint.Client.dll chargé et, en général, ne doit pas changer. Toutefois, vous pouvez modifier cette propriété si vous souhaitez utiliser les bibliothèques CSOM v16 dans un déploiement v15 (local).
  • Propriété Version : obtient la version de ce travail du minuteur. La version est initialement définie dans le constructeur de travail du minuteur ou la valeur par défaut est 1.0 lorsqu’elle n’est pas définie via le constructeur.

Pour préparer l’exécution d’un travail du minuteur, vous devez d’abord le configurer :

  1. Fournissez les paramètres d’authentification .
  2. Fournissez une étendue, qui est une liste de sites.
  3. Définissez éventuellement les propriétés du travail du minuteur.

Du point de vue de l’exécution, les étapes globales suivantes sont effectuées lors du démarrage d’une exécution de travail du minuteur :

  1. Résoudre les sites : les URL de site carte sauvages (par exemple, https://tenant.sharepoint.com/sites/d*) sont résolues dans une liste réelle de sites existants. Si le développement du sous-site a été demandé, la liste des sites résolus est développée avec tous les sous-sites.
  2. Créez des lots de travail en fonction des paramètres de bande de roulement actuels et créez un thread par lot.
  3. Les threads exécutent des lots de travail et appellent l’événement TimerJobRun pour chaque site de la liste.

Pour plus d’informations sur chaque étape, consultez les sections suivantes.

Authentification

Avant qu’un travail du minuteur puisse être utilisé, le travail du minuteur doit savoir comment s’authentifier auprès de SharePoint. L’infrastructure prend actuellement en charge les approches dans l’énumération AuthenticationType : Office365, NetworkCredentials et AppOnly. L’utilisation des méthodes suivantes définit également automatiquement la propriété AuthenticationType sur la valeur appropriée d’Office365, NetworkCredentials et AppOnly.

L’organigramme suivant montre les étapes à suivre, suivies d’explications détaillées de chaque approche.

Organigramme des étapes d’authentification

Informations d’identification de l’utilisateur

Pour spécifier les informations d’identification utilisateur à exécuter sur Office 365, vous pouvez utiliser ces deux méthodes :

public void UseOffice365Authentication(string userUPN, string password)
public void UseOffice365Authentication(string credentialName)

La première méthode accepte un nom d’utilisateur et un mot de passe. La deuxième vous permet de spécifier des informations d’identification génériques stockées dans le Gestionnaire d’informations d’identification Windows. La capture d’écran suivante montre les informations d’identification bertonline génériques. Pour l’utiliser pour authentifier le travail du minuteur, indiquez bertonline comme paramètre de la deuxième méthode.

Gestionnaire d’informations d’identification Windows


Il existe des méthodes similaires pour l’exécution sur SharePoint en local :

public void UseNetworkCredentialsAuthentication(string samAccountName, string password, string domain)
public void UseNetworkCredentialsAuthentication(string credentialName)

Application uniquement

L’authentification d’application uniquement est la méthode recommandée , car vous pouvez accorder des autorisations étendues au locataire. Pour les informations d’identification de l’utilisateur, le compte d’utilisateur doit disposer des autorisations nécessaires.

Remarque

Certaines logiques de résolution de site ne fonctionnent pas avec l’authentification d’application uniquement. Vous trouverez plus d’informations dans la section suivante.

Pour configurer le travail pour l’authentification d’application uniquement, utilisez l’une des méthodes suivantes :

public void UseAppOnlyAuthentication(string clientId, string clientSecret)
public void UseAzureADAppOnlyAuthentication(string clientId, string clientSecret)

La même méthode peut être utilisée pour Office 365 ou SharePoint localement, ce qui rend les travaux du minuteur qui utilisent l’authentification d’application uniquement facilement transportables entre les environnements.

Remarque

Lorsque vous utilisez l’authentification d’application uniquement, la logique du travail du minuteur échoue lorsque des API qui ne fonctionnent pas avec AuthenticationType.AppOnly sont utilisées. Les exemples classiques sont l’API de recherche, l’écriture dans le magasin de taxonomie et l’utilisation de l’API de profil utilisateur.

Sites sur lesquels opérer

Lorsqu’un travail du minuteur s’exécute, il doit s’exécuter sur un ou plusieurs sites.

Ajouter des sites à un travail de minuteur

Pour ajouter des sites à un travail de minuteur, utilisez l’ensemble de méthodes suivant :

public void AddSite(string site)
public void ClearAddedSites()

Pour ajouter un site, spécifiez une URL complète (par exemple, https://tenant.sharepoint.com/sites/dev) ou une URL carte générique.

Une URL carte générique est une URL qui se termine par un astérisque (*). Un seul caractère * est autorisé et il doit s’agir du dernier caractère de l’URL. Un exemple d’URL de carte générique est https://tenant.sharepoint.com/sites/*, qui retourne toutes les collections de sites sous le chemin d’accès managé de ce site. Pour un autre exemple, https://tenant.sharepoint.com/sites/dev* retourne toutes les collections de sites où l’URL contient dev.

En règle générale, les sites sont ajoutés par le programme qui instancie l’objet de travail du minuteur, mais si nécessaire, le travail du minuteur peut prendre le contrôle sur la liste des sites passée. Pour ce faire, ajoutez un remplacement de méthode pour la UpdateAddedSitesméthode virtuelle, comme indiqué dans l’exemple suivant :

public override List<string> UpdateAddedSites(List<string> addedSites)
{
    // Let's assume we're not happy with the provided list of sites, so first clear it
    addedSites.Clear();

    // Manually adding a new wildcard Url, without an added URL the timer job will do...nothing
    addedSites.Add("https://bertonline.sharepoint.com/sites/d*");

    // Return the updated list of sites
    return addedSites;
}

Spécifier les informations d’identification d’énumération

Après avoir ajouté une URL de carte générique et défini l’authentification sur application uniquement, spécifiez les informations d’identification de l’énumération. Les informations d’identification d’énumération sont utilisées pour extraire une liste de collections de sites utilisées dans l’algorithme de correspondance de site afin de retourner une liste réelle de sites.

Pour obtenir une liste de collections de sites, l’infrastructure du minuteur se comporte différemment entre Office 365 (v16) et localement (v15) :

  • Office 365 : la méthode Tenant.GetSiteProperties est utilisée pour lire les collections de sites « régulières » ; l’API de recherche est utilisée pour lire les collections de sites OneDrive Entreprise.
  • SharePoint en local : l’API de recherche est utilisée pour lire toutes les collections de sites.

Étant donné que l’API de recherche ne fonctionne pas avec un contexte utilisateur, le travail du minuteur revient aux informations d’identification d’énumération spécifiées.

Pour spécifier les informations d’identification utilisateur à exécuter sur Office 365, vous pouvez utiliser ces deux méthodes :

public void SetEnumerationCredentials(string userUPN, string password)
public void SetEnumerationCredentials(string credentialName)

Il existe des méthodes similaires pour l’exécution sur SharePoint en local :

public void SetEnumerationCredentials(string samAccountName, string password, string domain)
public void SetEnumerationCredentials(string credentialName)

La première méthode accepte simplement un nom d’utilisateur, un mot de passe et éventuellement un domaine (en local). La seconde spécifie des informations d’identification génériques stockées dans le Gestionnaire d’informations d’identification Windows. Consultez la section Authentification pour en savoir plus sur le Gestionnaire d’informations d’identification.

Développement du sous-site

Souvent, vous souhaitez que le code du travail du minuteur soit exécuté sur le site racine de la collection de sites et sur tous les sous-sites de cette collection de sites. Pour ce faire, définissez la propriété ExpandSubSites sur true. Le travail du minuteur développe alors les sous-sites dans le cadre de l’étape de résolution du site.

Remplacer les sites résolus et/ou développés

Une fois que l’infrastructure du minuteur a résolu les sites carte génériques et éventuellement étendu les sous-sites, l’étape suivante consiste à traiter la liste des sites. Avant de traiter la liste des sites, vous pouvez modifier la liste des sites. Par exemple, vous pouvez supprimer des sites spécifiques ou ajouter d’autres sites à la liste. Pour ce faire, vous pouvez remplacer la méthode virtuelle ResolveAddedSites . L’exemple suivant montre comment remplacer la méthode ResolveAddedSites pour supprimer un site de la liste.

public override List<string> ResolveAddedSites(List<string> addedSites)
{
    // Use default TimerJob base class site resolving
    addedSites = base.ResolveAddedSites(addedSites);

    //Delete the first one from the list...simple change. A real life case could be reading the site scope
    //from a SQL (Azure) DB to prevent the whole site resolving.
    addedSites.RemoveAt(0);

    //Return the updated list of resolved sites...this list will be processed by the timer job
    return addedSites;
}

Événement TimerJobRun

L’infrastructure de travail du minuteur fractionne la liste des sites en lots de travail. Chaque lot de sites est exécuté sur son propre thread. Par défaut, l’infrastructure crée cinq lots et cinq threads pour exécuter ces cinq lots. Consultez la section Threading pour en savoir plus sur les options de thread de travail du minuteur.

Lorsqu’un thread traite un lot, l’événement TimerJobRun est déclenché par l’infrastructure du minuteur et fournit toutes les informations nécessaires pour exécuter le travail du minuteur. Les travaux du minuteur étant exécutés en tant qu’événements, le code doit connecter un gestionnaire d’événements à l’événement TimerJobRun :

public SimpleJob() : base("SimpleJob")
{
    TimerJobRun += SimpleJob_TimerJobRun;
}

void SimpleJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
{
    // your timer job logic goes here
}

Une autre approche consiste à utiliser un délégué inline, comme illustré ici :

public SimpleJob() : base("SimpleJob")
{
    // Inline delegate
    TimerJobRun += delegate(object sender, TimerJobRunEventArgs e)
    {
        // your timer job logic goes here
    };
}

Lorsque l’événement TimerJobRun se déclenche, vous recevez un objet TimerJobRunEventArgs , qui fournit les informations nécessaires pour écrire la logique du travail du minuteur. Les attributs et méthodes suivants sont disponibles dans cette classe :

Structure de classe TimerJobRunEventArgs

Plusieurs propriétés et toutes les méthodes sont utilisées dans la fonctionnalité de gestion d’état facultative, qui est décrite dans la section suivante. Toutefois, les propriétés suivantes sont toujours disponibles dans chaque événement, quelle que soit la configuration utilisée :

  • Propriété Url : obtient ou définit l’URL du site sur lequel le travail du minuteur doit fonctionner. Il peut s’agir du site racine de la collection de sites, mais il peut également s’agir d’un sous-site en cas de développement du site.
  • Propriété ConfigurationData : obtient ou définit des données de configuration de travail de minuteur supplémentaires (facultatif). Ces données de configuration sont transmises dans le cadre de l’objet TimerJobRunEventArgs .
  • Propriété WebClientContext : obtient ou définit l’objet ClientContext pour l’URL actuelle. Cette propriété est un objet ClientContext pour le site défini dans la propriété Url . Il s’agit généralement de l’objet ClientContext que vous utiliseriez dans le code de votre travail du minuteur.
  • Propriété SiteClientContext : obtient ou définit l’objet ClientContext pour le site racine de la collection de sites. Cette propriété permet d’accéder au site racine si le travail du minuteur nécessite un accès à celui-ci. Par exemple, le travail du minuteur peut ajouter une mise en page à la galerie de pages master à l’aide de la propriété SiteClientContext.
  • Propriété TenantClientContext : obtient ou définit l’objet ClientContext pour qu’il fonctionne avec l’API locataire. Cette propriété fournit un objet ClientContext construit à l’aide de l’URL du site d’administration du locataire. Pour utiliser l’API tenant dans le gestionnaire d’événements TimerJobRun du travail du minuteur, créez un objet Tenant à l’aide de cette propriété TenantClientContext .

Tous les objets ClientContext utilisent les informations d’authentification décrites dans la section Authentification . Si vous avez choisi les informations d’identification de l’utilisateur, vérifiez que le compte utilisé dispose des autorisations nécessaires pour fonctionner sur les sites spécifiés. Lors de l’utilisation de l’application uniquement, il est préférable de définir des autorisations d’étendue client sur le principal d’application uniquement.

Gestion de l’état

Lorsque vous écrivez une logique de travail du minuteur, vous devez souvent conserver l’état ; par exemple, pour enregistrer la date du dernier traitement d’un site ou pour stocker des données pour prendre en charge la logique métier de votre travail du minuteur. Pour cette raison, l’infrastructure de travail du minuteur dispose de fonctionnalités de gestion d’état.

La gestion de l’état stocke et récupère un ensemble de propriétés standard et personnalisées sous forme de chaîne sérialisée JSON dans le conteneur de propriétés web du site traité (nom = nom du travail du minuteur + « _Properties »). Voici les propriétés par défaut de l’objet TimerJobRunEventArgs :

  • Propriété PreviousRun : obtient ou définit la date et l’heure de l’exécution précédente.
  • Propriété PreviousRunSuccessful : obtient ou définit une valeur indiquant si l’exécution précédente a réussi. Notez que l’auteur du travail du minuteur est chargé de marquer l’exécution d’un travail comme réussie en définissant la propriété CurrentRunSuccessful dans le cadre de l’implémentation de votre travail du minuteur.
  • Propriété PreviousRunVersion : obtient ou définit la version du travail du minuteur de l’exécution précédente.

En regard de ces propriétés standard, vous avez également la possibilité de spécifier vos propres propriétés en ajoutant des paires mot clé-valeur à la collection Properties de l’objet TimerJobRunEventArgs. Pour faciliter cela, il existe trois méthodes pour vous aider :

  • SetProperty ajoute ou met à jour une propriété.
  • GetProperty retourne la valeur d’une propriété.
  • DeleteProperty supprime une propriété de la collection de propriétés.

Le code suivant montre comment la gestion d’état peut être utilisée :

void SiteGovernanceJob_TimerJobRun(object o, TimerJobRunEventArgs e)
{
    try
    {
        string library = "";

        // Get the number of admins
        var admins = e.WebClientContext.Web.GetAdministrators();

        Log.Info("SiteGovernanceJob", "ThreadID = {2} | Site {0} has {1} administrators.", e.Url, admins.Count, Thread.CurrentThread.ManagedThreadId);

        // grab reference to list
        library = "SiteAssets";
        List list = e.WebClientContext.Web.GetListByUrl(library);

        if (!e.GetProperty("ScriptFileVersion").Equals("1.0", StringComparison.InvariantCultureIgnoreCase))
        {
            if (list == null)
            {
                // grab reference to list
                library = "Style%20Library";
                list = e.WebClientContext.Web.GetListByUrl(library);
            }

            if (list != null)
            {
                // upload js file to list
                list.RootFolder.UploadFile("sitegovernance.js", "sitegovernance.js", true);

                e.SetProperty("ScriptFileVersion", "1.0");
            }
        }

        if (admins.Count < 2)
        {
            // Oops, we need at least 2 site collection administrators
            e.WebClientContext.Site.AddJsLink(SiteGovernanceJobKey, BuildJavaScriptUrl(e.Url, library));
            Console.WriteLine("Site {0} marked as incompliant!", e.Url);
            e.SetProperty("SiteCompliant", "false");
        }
        else
        {
            // We're all good...let's remove the notification
            e.WebClientContext.Site.DeleteJsLink(SiteGovernanceJobKey);
            Console.WriteLine("Site {0} is compliant", e.Url);
            e.SetProperty("SiteCompliant", "true");
        }

        e.CurrentRunSuccessful = true;
        e.DeleteProperty("LastError");
    }
    catch(Exception ex)
    {
        e.CurrentRunSuccessful = false;
        e.SetProperty("LastError", ex.Message);
    }
}

L’état est stocké sous la forme d’une seule propriété JSON sérialisée, ce qui signifie qu’il peut également être utilisé par d’autres personnalisations. Par exemple, si le travail du minuteur a écrit l’entrée d’état « SiteCompliant=false », une routine JavaScript peut inviter l’utilisateur à agir, car le travail du minuteur a déterminé que le site n’était pas conforme.

Filetage

Par défaut, l’infrastructure du travail du minuteur utilise des threads pour paralléliser le travail. Le threading est utilisé à la fois pour l’expansion du sous-site (si demandé) et pour l’exécution de la logique de travail du minuteur réel (événement TimerJobRun ) pour chaque site. Les propriétés suivantes peuvent être utilisées pour contrôler l’implémentation du thread :

  • Propriété UseThreading : obtient ou définit une valeur indiquant si le threading est utilisé. Par défaut est vrai. Définissez sur false pour effectuer toutes les actions à l’aide du thread d’application main.
  • Propriété MaximumThreads : obtient ou définit le nombre de threads à utiliser pour ce travail du minuteur. Les valeurs valides sont comprises entre 2 et 100. La valeur par défaut est 5. Avoir un grand nombre de threads n’est pas nécessairement plus rapide que d’avoir seulement quelques threads. Le nombre optimal doit être acquis par le biais de tests à l’aide d’une variété de nombres de threads. La valeur par défaut de 5 threads a été trouvée pour améliorer considérablement les performances dans la plupart des scénarios.

Limitation

Étant donné qu’un travail du minuteur utilise des opérations de thread et de travail du minuteur sont généralement des opérations gourmandes en ressources, l’exécution d’un travail du minuteur peut être limitée. Pour gérer correctement la limitation, l’infrastructure du travail du minuteur et l’ensemble de PnP Core utilisent la méthode ExecuteQueryRetry au lieu de la méthode ExecuteQuery par défaut.

Remarque

Il est important d’utiliser ExecuteQueryRetry dans le code d’implémentation de votre travail du minuteur.

Problèmes d’accès concurrentiel : traiter tous les sous-sites d’une collection de sites dans le même thread

Les travaux du minuteur peuvent rencontrer des problèmes d’accès concurrentiel lors de l’utilisation de plusieurs threads pour traiter des sous-sites.

Prenons cet exemple : le thread A traite le premier ensemble de sous-sites de la collection de sites 1, et le thread B traite le reste des sous-sites de la collection de sites 1. Si le travail du minuteur traite le sous-site et le site racine (à l’aide de l’objet SiteClientContext ), il peut y avoir un problème d’accès concurrentiel, car les threads A et B traitent le site racine.

Pour éviter le problème d’accès concurrentiel (sans exécuter les travaux du minuteur dans un seul thread), utilisez la méthode GetAllSubSites dans le travail du minuteur.

Le code suivant montre comment utiliser la méthode GetAllSubSites dans un travail du minuteur :

public class SiteCollectionScopedJob: TimerJob
{
    public SiteCollectionScopedJob() : base("SiteCollectionScopedJob")
    {
        // ExpandSites *must* be false as we'll deal with that at TimerJobEvent level
        ExpandSubSites = false;
        TimerJobRun += SiteCollectionScopedJob_TimerJobRun;
    }

    void SiteCollectionScopedJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
    {
        // Get all the subsites in the site we're processing
        IEnumerable<string> expandedSites = GetAllSubSites(e.SiteClientContext.Site);

        // Manually iterate over the content
        foreach (string site in expandedSites)
        {
            // Clone the existing ClientContext for the sub web
            using (ClientContext ccWeb = e.SiteClientContext.Clone(site))
            {
                // Here's the timer job logic, but now a single site collection is handled in a single thread which
                // allows for further optimization or prevents race conditions
                ccWeb.Load(ccWeb.Web, s => s.Title);
                ccWeb.ExecuteQueryRetry();
                Console.WriteLine("Here: {0} - {1}", site, ccWeb.Web.Title);
            }
        }
    }
}

Journalisation

L’infrastructure de travail du minuteur utilise les composants de journalisation PnP Core, car elle fait partie de la bibliothèque PnP Core. Pour activer la journalisation PnP Core intégrée, configurez-la à l’aide du fichier de configuration approprié (app.config ou web.config). L’exemple suivant montre la syntaxe requise :

  <system.diagnostics>
    <trace autoflush="true" indentsize="4">
      <listeners>
        <add name="DebugListenter" type="System.Diagnostics.TextWriterTraceListener" initializeData="trace.log" />
        <!--<add name="consoleListener" type="System.Diagnostics.ConsoleTraceListener" />-->
      </listeners>
    </trace>
  </system.diagnostics>

À l’aide du fichier de configuration ci-dessus, l’infrastructure de travail du minuteur utilise pour écrire des System.Diagnostics.TextWriterTraceListener journaux dans un fichier appelé trace.log dans le même dossier que le travail du minuteur .exe. D’autres écouteurs de suivi sont disponibles, tels que :

Il est fortement recommandé d’utiliser la même approche de journalisation pour votre code de travail de minuteur personnalisé que pour l’infrastructure de travail du minuteur. Dans le code de votre travail du minuteur, vous pouvez utiliser la classe PnP Core Log :

void SiteGovernanceJob_TimerJobRun(object o, TimerJobRunEventArgs e)
{
    try
    {
        string library = "";

        // Get the number of admins
        var admins = e.WebClientContext.Web.GetAdministrators();

        Log.Info("SiteGovernanceJob", "ThreadID = {2} | Site {0} has {1} administrators.", e.Url, admins.Count, Thread.CurrentThread.ManagedThreadId);

        // Additional timer job logic...

        e.CurrentRunSuccessful = true;
        e.DeleteProperty("LastError");
    }
    catch(Exception ex)
    {
        Log.Error("SiteGovernanceJob", "Error while processing site {0}. Error = {1}", e.Url, ex.Message);
        e.CurrentRunSuccessful = false;
        e.SetProperty("LastError", ex.Message);
    }
}

Voir aussi