Compartir a través de


Marco de trabajo del temporizador PnP

El marco de trabajo del temporizador PnP es un conjunto de clases diseñadas para facilitar la creación de procesos en segundo plano que funcionan con sitios de SharePoint. El marco de trabajo del temporizador es similar a los trabajos de temporizador de código de plena confianza locales (SPJobDefinition). La principal diferencia entre el marco de trabajo del temporizador y el trabajo del temporizador de código de plena confianza es que el marco de trabajo del temporizador solo usa las API del lado cliente y, por lo tanto, puede (y debe) ejecutarse fuera de SharePoint. El marco de trabajo del temporizador permite crear trabajos de temporizador que funcionan con SharePoint Online.

Una vez creado un trabajo de temporizador, debe programarse y ejecutarse. Las dos opciones más comunes son:

  • Cuando Microsoft Azure es la plataforma de hospedaje, los trabajos del temporizador se pueden implementar y ejecutar como Azure WebJobs.
  • Cuando Windows Server es la plataforma de hospedaje (por ejemplo, para SharePoint local), los trabajos del temporizador se pueden implementar y ejecutar en Windows Scheduler.

Para ver una introducción de vídeo a los trabajos del temporizador, vea el vídeo Introducción al marco de trabajo del temporizador PnP, que presenta el marco de trabajo del temporizador y muestra el ejemplo de trabajo de temporizador simple.

Ejemplo de trabajo de temporizador simple

En esta sección, aprenderá a crear un trabajo de temporizador muy sencillo. El objetivo de este ejemplo es proporcionar al lector una vista rápida; más adelante se proporciona una explicación más detallada del marco de trabajo del temporizador.

Nota:

Para obtener una solución PnP más amplia con diez ejemplos de trabajos de temporizador individuales, desde ejemplos de "Hola mundo" hasta trabajos reales de expiración de contenido, consulte Core.TimerJobs.Samples.

En los pasos siguientes se describe cómo crear un trabajo de temporizador simple.

Paso 1: Crear un proyecto de consola y hacer referencia a PnP Core

Cree un nuevo proyecto del tipo "consola" y haga referencia a la biblioteca PnP Core mediante una de las siguientes acciones:

  • Agregue el paquete NuGet Office 365 Developer Patterns and Practices Core al proyecto. Hay un paquete NuGet para v15 (local) y para v16 (Office 365). Esta es la opción preferida.

  • Agregue el proyecto de origen de PnP Core existente al proyecto. Esto le permite ir paso a paso por el código principal de PnP al depurar.

    Nota:

    Será responsable de mantener actualizado este código con los cambios más recientes agregados a PnP.

Paso 2: Crear una clase de trabajo de temporizador y agregar la lógica de trabajo del temporizador

  1. Agregue una clase para el trabajo del temporizador denominado SimpleJob.

  2. Hacer que la clase herede la clase base abstracta TimerJob .

  3. En el constructor, asigne al trabajo del temporizador un nombre (base("SimpleJob")) y conecte el controlador de eventos TimerJobRun .

  4. Agregue la lógica del trabajo del temporizador al controlador de eventos TimerJobRun .

El resultado será similar al siguiente:

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);
        }
    }
}

Paso 3: Actualizar Program.cs para usar el trabajo del temporizador

El trabajo del temporizador creado en el paso anterior todavía debe ejecutarse. Para ello, actualice Program.cs mediante los pasos siguientes:

  1. Cree una instancia de la clase de trabajo del temporizador.

  2. Proporcione los detalles de autenticación para el trabajo del temporizador. En este ejemplo se usa el nombre de usuario y la contraseña para autenticarse en SharePoint Online.

  3. Agregue uno o varios sitios al programa de trabajo del temporizador al que acceder. En este ejemplo se usa un carácter comodín en la dirección URL. El trabajo del temporizador se ejecuta en todos los sitios que coinciden con esta dirección URL de comodín.

  4. Inicie el trabajo del temporizador llamando al método 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();
}

Opciones de implementación del trabajo del temporizador

En el paso anterior se muestra un trabajo de temporizador simple. El siguiente paso es implementar el trabajo del temporizador.

Un trabajo de temporizador es un archivo .exe que debe programarse en una plataforma de hospedaje. En función de la plataforma de hospedaje elegida, la implementación difiere. En las secciones siguientes se describen las dos opciones de plataforma de hospedaje más comunes:

  • Uso de Azure como plataforma de hospedaje.
  • Usar Windows Server como plataforma de hospedaje.

Implementación de trabajos de temporizador en Azure mediante Azure WebJobs

Antes de implementar un trabajo de temporizador, asegúrese de que el trabajo se puede ejecutar sin interacción del usuario. En el ejemplo de este artículo se solicita al usuario que proporcione una contraseña o un secreto de cliente (consulte más información en la sección Autenticación ), que funciona durante las pruebas, pero no funciona cuando se implementa. Todos los ejemplos existentes permiten al usuario proporcionar una contraseña o un secreto de cliente mediante el archivo 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>

Después de agregar estos cambios al archivo app.config, ejecute el trabajo del temporizador desde Visual Studio para confirmar que se ejecuta sin interacción del usuario.

La implementación real en Azure se basa en Azure WebJobs. Para implementar este ejemplo de trabajo del temporizador, siga estos pasos:

  1. Haga clic con el botón derecho en el proyecto en Visual Studio y elija Publicar como Azure WebJob.

  2. Proporcione una programación para el trabajo del temporizador y, a continuación, elija Aceptar.

  3. Elija Sitios web de Microsoft Azure como destino de publicación. Se le pedirá que inicie sesión en Azure y seleccione el sitio web de Azure que hospedará el trabajo del temporizador (también puede crear uno nuevo si fuera necesario).

  4. Elija Publicar para insertar WebJob en Azure.

  5. Una vez publicado el trabajo del temporizador, puede desencadenar el trabajo y comprobar la ejecución del trabajo desde Visual Studio o el Azure Portal.

    Azure Portal (heredado)

  6. Además, el trabajo del temporizador se puede ejecutar desde el nuevo Azure Portal seleccionando el trabajo y eligiendo Ejecutar. Puede encontrar más detalles sobre cómo trabajar con WebJobs desde el nuevo portal en el artículo Ejecución de tareas en segundo plano con WebJobs en Azure App Service.

    Azure Portal (actual)

Nota:

Para obtener instrucciones detalladas sobre la implementación de un trabajo web de Azure, consulte Introducción a Azure WebJobs (trabajos del temporizador) para los sitios de Office 365.

Implementación de trabajos del temporizador en Windows Server mediante el Programador de Windows

Cuando se implementa en Windows Server, el trabajo del temporizador debe ejecutarse sin interacción del usuario.

  1. Modifique el archivo app.config como se describe en la sección anterior Implementación de trabajos del temporizador en Azure mediante Azure WebJobs.

  2. Copie la versión de lanzamiento del trabajo en el servidor en el que desea que se ejecute.

    Importante

    Copie todos los ensamblados pertinentes, el archivo .exe y el archivo .config para asegurarse de que el trabajo se puede ejecutar en el servidor sin instalar ningún archivo o programa adicional en el servidor.

  3. Programe la ejecución del trabajo del temporizador. Se recomienda usar el Programador de tareas de Windows integrado. Para usar el Programador de tareas de Windows:

    1. Abra el Programador de tareas (Panel de control>Programador de tareas).
    2. Elija Crear tarea y especifique un nombre y una cuenta que ejecutarán la tarea.
    3. Elija Desencadenadores y agregue un nuevo desencadenador. Especifique la programación que desea para el trabajo del temporizador.
    4. Elija Acciones y elija la acción Iniciar un programa, seleccione el trabajo del temporizador .exe archivo y, a continuación, establezca el inicio en la carpeta.
    5. Elija Aceptar para guardar la tarea.

Programador de tareas de Windows

Marco de trabajo del temporizador en profundidad

En esta sección se detallan las características del marco de trabajo del temporizador y cómo funcionan.

Estructura

La clase TimerJob es una clase base abstracta que contiene las siguientes propiedades, métodos y eventos públicos:

Estructura de clase TimerJob

La mayoría de las propiedades y métodos se explican con más detalle en las secciones siguientes. El resto de las propiedades y métodos se describen aquí:

  • Propiedad IsRunning : obtiene un valor que indica si el trabajo del temporizador se está ejecutando. Valor de true si se ejecuta; false si no se está ejecutando.
  • Propiedad Name : obtiene el nombre del trabajo del temporizador. El nombre se establece inicialmente en el constructor del trabajo del temporizador.
  • Propiedad SharePointVersion : obtiene o establece la versión de SharePoint. Esta propiedad se establece automáticamente en función de la versión del Microsoft.SharePoint.Client.dll cargado y, en general, no debe cambiar. Sin embargo, puede cambiar esta propiedad en caso de que desee usar las bibliotecas de CSOM v16 en una implementación v15 (local).
  • Propiedad Version : obtiene la versión de este trabajo del temporizador. La versión se establece inicialmente en el constructor de trabajos del temporizador o el valor predeterminado es 1.0 cuando no se establece a través del constructor.

Para preparar la ejecución de un trabajo del temporizador, primero debe configurarlo:

  1. Proporcione la configuración de autenticación .
  2. Proporcione un ámbito, que es una lista de sitios.
  3. Opcionalmente, establezca las propiedades del trabajo del temporizador.

Desde una perspectiva de ejecución, se realizan los siguientes pasos generales cuando se inicia una ejecución de trabajo del temporizador:

  1. Resolver sitios: las direcciones URL de sitio de comodín (por ejemplo, https://tenant.sharepoint.com/sites/d*) se resuelven en una lista real de sitios existentes. Si se solicitó la expansión del subsitio, la lista de sitios resueltos se expande con todos los subsitios.
  2. Cree lotes de trabajo en función de la configuración de banda de rodadura actual y cree un subproceso por lote.
  3. Los subprocesos ejecutan lotes de trabajo y llaman al evento TimerJobRun para cada sitio de la lista.

Puede encontrar más detalles sobre cada paso en las secciones siguientes.

Autenticación

Antes de que se pueda usar un trabajo de temporizador, el trabajo del temporizador debe saber cómo autenticarse de nuevo en SharePoint. El marco admite actualmente los enfoques de la enumeración AuthenticationType : Office365, NetworkCredentials y AppOnly. Con los métodos siguientes también se establece automáticamente la propiedad AuthenticationType en el valor adecuado de Office365, NetworkCredentials y AppOnly.

En el diagrama de flujo siguiente se muestran los pasos que se van a seguir, seguidos de explicaciones detalladas de cada enfoque.

Diagrama de flujo de los pasos de autenticación

Credenciales de usuario

Para especificar las credenciales de usuario para ejecutarse en Office 365, puede usar estos dos métodos:

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

El primer método acepta un nombre de usuario y una contraseña. El segundo permite especificar una credencial genérica almacenada en el Administrador de credenciales de Windows. En la captura de pantalla siguiente se muestra la bertonline credencial genérica. Para usarlo para autenticar el trabajo del temporizador, proporcione bertonline como parámetro del segundo método.

Administrador de credenciales de Windows


Hay métodos similares para ejecutarse en SharePoint local:

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

Solo aplicación

La autenticación de solo aplicación es el método preferido , ya que puede conceder permisos de ámbito de inquilino. Para las credenciales de usuario, la cuenta de usuario debe tener los permisos necesarios.

Nota:

Cierta lógica de resolución de sitios no funcionará con la autenticación de solo aplicación. Los detalles se pueden encontrar en la sección siguiente.

Para configurar el trabajo para la autenticación de solo aplicación, use uno de los métodos siguientes:

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

El mismo método se puede usar para Office 365 o SharePoint local, lo que hace que los trabajos del temporizador que usan la autenticación de solo aplicación se puedan transportar fácilmente entre entornos.

Nota:

Cuando se usa la autenticación solo de aplicación, se produce un error en la lógica del trabajo del temporizador cuando se usan API que no funcionan con AuthenticationType.AppOnly. Los ejemplos típicos son Search API, la escritura en el almacén de taxonomía y el uso de la API de perfil de usuario.

Sitios en los que operar

Cuando se ejecuta un trabajo del temporizador, necesita uno o varios sitios en los que ejecutarse.

Adición de sitios a un trabajo de temporizador

Para agregar sitios a un trabajo de temporizador, use el siguiente conjunto de métodos:

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

Para agregar un sitio, especifique una dirección URL completa (por ejemplo, https://tenant.sharepoint.com/sites/dev) o una dirección URL de comodín.

Una dirección URL de comodín es una dirección URL que termina con un asterisco (*). Solo se permite un único * y debe ser el último carácter de la dirección URL. Una dirección URL de comodín de ejemplo es https://tenant.sharepoint.com/sites/*, que devuelve todas las colecciones de sitios en la ruta de acceso administrada de ese sitio. Por otro ejemplo, https://tenant.sharepoint.com/sites/dev* devuelve todas las colecciones de sitios donde la dirección URL contiene dev.

Normalmente, el programa que crea instancias del objeto de trabajo del temporizador agrega los sitios, pero, si es necesario, el trabajo del temporizador puede tomar el control sobre la lista pasada de sitios. Para ello, agregue una invalidación de método para el UpdateAddedSitesmétodo virtual, como se muestra en el ejemplo siguiente:

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;
}

Especificar credenciales de enumeración

Después de agregar una dirección URL de comodín y establecer la autenticación en solo aplicación, especifique las credenciales de enumeración. Las credenciales de enumeración se usan para capturar una lista de colecciones de sitios que se usan en el algoritmo de coincidencia de sitios para devolver una lista real de sitios.

Para adquirir una lista de colecciones de sitios, el marco del temporizador se comporta de forma diferente entre Office 365 (v16) y local (v15):

  • Office 365: el método Tenant.GetSiteProperties se usa para leer las colecciones de sitios "normales"; la API de búsqueda se usa para leer las colecciones de sitios de OneDrive para la Empresa.
  • SharePoint local: la API de búsqueda se usa para leer todas las colecciones de sitios.

Dado que la API de búsqueda no funciona con un contexto de usuario, el trabajo del temporizador vuelve a las credenciales de enumeración especificadas.

Para especificar las credenciales de usuario para ejecutarse en Office 365, puede usar estos dos métodos:

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

Hay métodos similares para ejecutarse en SharePoint local:

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

El primer método simplemente acepta un nombre de usuario, una contraseña y, opcionalmente, un dominio (cuando está en el entorno local). El segundo especifica una credencial genérica almacenada en el Administrador de credenciales de Windows. Consulte la sección Autenticación para obtener más información sobre el Administrador de credenciales.

Expansión del subsitio

A menudo, quiere que el código de trabajo del temporizador se ejecute en el sitio raíz de la colección de sitios y en todos los subsitios de esa colección de sitios. Para ello, establezca la propiedad ExpandSubSites en true. Esto hace que el trabajo del temporizador expanda los subsitios como parte del paso de resolución del sitio.

Invalidar sitios resueltos o expandidos

Una vez que el marco del temporizador resuelve los sitios comodín y, opcionalmente, expande los subsitios, el siguiente paso es procesar la lista de sitios. Antes de procesar la lista de sitios, es posible que desee modificar la lista de sitios. Por ejemplo, es posible que quiera quitar sitios específicos o agregar más sitios a la lista. Esto se puede lograr reemplazando el método virtual ResolveAddedSites . En el ejemplo siguiente se muestra cómo invalidar el método ResolveAddedSites para quitar un sitio de la lista.

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;
}

Evento TimerJobRun

El marco de trabajo del temporizador divide la lista de sitios en lotes de trabajo. Cada lote de sitios se ejecuta en su propio subproceso. De forma predeterminada, el marco crea cinco lotes y cinco subprocesos para ejecutar esos cinco lotes. Consulte la sección Subprocesos para obtener más información sobre las opciones de subprocesos de trabajos del temporizador.

Cuando un subproceso procesa un lote, el marco del temporizador desencadena el evento TimerJobRun y proporciona toda la información necesaria para ejecutar el trabajo del temporizador. Los trabajos del temporizador se ejecutan como eventos, por lo que el código debe conectar un controlador de eventos al evento TimerJobRun :

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

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

Un enfoque alternativo usa un delegado insertado como se muestra aquí:

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

Cuando se desencadena el evento TimerJobRun , recibe un objeto TimerJobRunEventArgs , que proporciona la información necesaria para escribir la lógica del trabajo del temporizador. Los siguientes atributos y métodos están disponibles en esta clase:

Estructura de clase TimerJobRunEventArgs

Varias de las propiedades y todos los métodos se usan en la característica de administración de estado opcional, que se describe en la sección siguiente. Sin embargo, las siguientes propiedades siempre están disponibles en todos los eventos, independientemente de la configuración usada:

  • Propiedad Url : obtiene o establece la dirección URL del sitio para que funcione el trabajo del temporizador. Este puede ser el sitio raíz de la colección de sitios, pero también puede ser un subsitio en caso de que se haya realizado la expansión del sitio.
  • Propiedad ConfigurationData : obtiene o establece datos adicionales de configuración del trabajo del temporizador (opcional). Estos datos de configuración se pasan como parte del objeto TimerJobRunEventArgs .
  • Propiedad WebClientContext : obtiene o establece el objeto ClientContext para la dirección URL actual. Esta propiedad es un objeto ClientContext para el sitio definido en la propiedad Url . Este suele ser el objeto ClientContext que se usaría en el código de trabajo del temporizador.
  • Propiedad SiteClientContext : obtiene o establece el objeto ClientContext para el sitio raíz de la colección de sitios. Esta propiedad proporciona acceso al sitio raíz si el trabajo del temporizador requiere acceso a él. Por ejemplo, el trabajo del temporizador puede agregar un diseño de página a la galería de páginas maestras mediante la propiedad SiteClientContext .
  • Propiedad TenantClientContext : obtiene o establece el objeto ClientContext para que funcione con la API tenant. Esta propiedad proporciona un objeto ClientContext construido mediante la dirección URL del sitio de administración del inquilino. Para usar la API de inquilino en el controlador de eventos TimerJobRun del trabajo del temporizador, cree un nuevo objeto Tenant mediante esta propiedad TenantClientContext .

Todos los objetos ClientContext usan la información de autenticación descrita en la sección Autenticación . Si ha optado por las credenciales de usuario, asegúrese de que la cuenta usada tenga los permisos necesarios para operar en los sitios especificados. Cuando se usa solo aplicación, es mejor establecer permisos de ámbito de inquilino en la entidad de seguridad de solo aplicación.

Administración de estados

Al escribir lógica de trabajo del temporizador, a menudo debe conservar el estado; por ejemplo, para registrar cuándo se procesó por última vez un sitio o para almacenar datos que admitan la lógica de negocios del trabajo del temporizador. Por este motivo, el marco de trabajo del temporizador tiene funcionalidades de administración de estado.

La administración de estado almacena y recupera un conjunto de propiedades estándar y personalizadas como una cadena serializada JSON en el contenedor de propiedades web del sitio procesado (nombre = nombre del trabajo del temporizador + "_Properties"). A continuación se muestran las propiedades predeterminadas del objeto TimerJobRunEventArgs :

  • Propiedad PreviousRun : obtiene o establece la fecha y hora de la ejecución anterior.
  • Propiedad PreviousRunSuccessful : obtiene o establece un valor que indica si la ejecución anterior se realizó correctamente. Tenga en cuenta que el autor del trabajo del temporizador es responsable de marcar una ejecución de trabajo como correcta estableciendo la propiedad CurrentRunSuccessful como parte de la implementación del trabajo del temporizador.
  • Propiedad PreviousRunVersion : obtiene o establece la versión del trabajo del temporizador de la ejecución anterior.

Junto a estas propiedades estándar, también tiene la opción de especificar sus propias propiedades agregando pares palabras clave-valor a la colección Properties del objeto TimerJobRunEventArgs . Para que esto sea más fácil, hay tres métodos que le ayudarán:

  • SetProperty agrega o actualiza una propiedad.
  • GetProperty devuelve el valor de una propiedad.
  • DeleteProperty quita una propiedad de la colección de propiedades.

En el código siguiente se muestra cómo se puede usar la administración de estados:

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);
    }
}

El estado se almacena como una única propiedad serializada JSON, lo que significa que también puede ser utilizado por otras personalizaciones. Por ejemplo, si el trabajo del temporizador escribió la entrada de estado "SiteCompliant=false", una rutina de JavaScript podría pedir al usuario que actuara porque el trabajo del temporizador determinó que el sitio no era conforme.

Threading

El marco de trabajo del temporizador usa de forma predeterminada subprocesos para paralelizar el trabajo. El subproceso se usa tanto para la expansión del subsitio (cuando se solicita) como para ejecutar la lógica de trabajo del temporizador real (evento TimerJobRun ) para cada sitio. Las siguientes propiedades se pueden usar para controlar la implementación de subprocesos:

  • Propiedad UseThreading : obtiene o establece un valor que indica si se usa el subproceso. Valores predeterminados de verdadero. Establézcalo en false para realizar todas las acciones mediante el subproceso de aplicación principal.
  • Propiedad MaximumThreads : obtiene o establece el número de subprocesos que se van a usar para este trabajo del temporizador. Los valores válidos son de 2 a 100. El valor predeterminado es 5. Tener muchos subprocesos no es necesariamente más rápido que tener solo unos cuantos subprocesos. El número óptimo se debe adquirir mediante pruebas mediante una variedad de recuentos de subprocesos. El valor predeterminado de 5 subprocesos se ha encontrado para aumentar significativamente el rendimiento en la mayoría de los escenarios.

Limitación

Dado que un trabajo de temporizador usa operaciones de trabajo de subproceso y temporizador suelen ser operaciones que consumen muchos recursos, se podría limitar la ejecución de un trabajo del temporizador. Para tratar correctamente la limitación, el marco de trabajo del temporizador y el conjunto de PnP Core usan el método ExecuteQueryRetry en lugar del método ExecuteQuery predeterminado.

Nota:

Es importante usar ExecuteQueryRetry en el código de implementación del trabajo del temporizador.

Problemas de simultaneidad: procesar todos los subsitios de una colección de sitios en el mismo subproceso

Los trabajos del temporizador pueden tener problemas de simultaneidad al usar varios subprocesos para procesar subsitios.

Tome este ejemplo: el subproceso A procesa el primer conjunto de subsitios de la colección de sitios 1 y el subproceso B procesa el resto de los subsitios de la colección de sitios 1. Si el trabajo del temporizador procesa el subsitio y el sitio raíz (mediante el objeto SiteClientContext ), podría haber un problema de simultaneidad porque tanto el subproceso A como el subproceso B procesan el sitio raíz.

Para evitar el problema de simultaneidad (sin ejecutar los trabajos del temporizador en un único subproceso), use el método GetAllSubSites dentro del trabajo del temporizador.

En el código siguiente se muestra cómo usar el método GetAllSubSites dentro de un trabajo de temporizador:

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);
            }
        }
    }
}

Registro

El marco de trabajo del temporizador usa los componentes de registro de PnP Core porque forma parte de la biblioteca PnP Core. Para activar el registro de PnP Core integrado, configúrelo mediante el archivo de configuración adecuado (app.config o web.config). En el ejemplo siguiente se muestra la sintaxis necesaria:

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

Con el archivo de configuración anterior, el marco de trabajo del temporizador usa System.Diagnostics.TextWriterTraceListener para escribir registros en un archivo denominado trace.log en la misma carpeta que el trabajo del temporizador .exe. Hay otros agentes de escucha de seguimiento disponibles, como:

Se recomienda encarecidamente usar el mismo enfoque de registro para el código de trabajo del temporizador personalizado que para el marco de trabajo del temporizador. En el código de trabajo del temporizador, puede usar la clase 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);
    }
}

Vea también