次の方法で共有


PnP タイマー ジョブ フレームワーク

PnP タイマー ジョブ フレームワークは、SharePoint サイトに対して動作するバック グラウンド プロセスの作成を容易にするために設計されたクラスのセットです。 タイマー ジョブ フレームワークは、オンプレミスの完全信頼コード タイマー ジョブ (SPJobDefinition) に似ています。 タイマー ジョブ フレームワークと完全信頼コード タイマー ジョブの主な相違点は、タイマー ジョブ フレームワークはクライアント側 API のみを使用するため、SharePoint の外部で実行できる (かつ実行する必要がある) ことです。 タイマー ジョブ フレームワークを使用することで、SharePoint Online に対して動作するタイマー ジョブの作成が可能になります。

タイマー ジョブを作成したら、スケジュールを設定して実行する必要があります。 2 つの最も一般的なオプションは、次のとおりです。

  • ホスティング プラットフォームが Microsoft Azure の場合、タイマー ジョブを Azure WebJobs として展開して実行できます。
  • ホスティング プラットフォームが Windows Server の場合 (オンプレミスの SharePoint の場合など)、タイマー ジョブは Windows スケジューラで展開して実行できます。

タイマー ジョブの概要については、「PnP タイマー ジョブ フレームワークの概要」というビデオを参照してください。このビデオでは、タイマー ジョブ フレームワークを紹介し、タイマー ジョブの単純な例を示します。

タイマー ジョブの単純な例

このセクションでは、非常に単純なタイマー ジョブを作成する方法を示します。 このサンプルの目的は読者にクイック ビューを提供することです。タイマー ジョブ フレームワークの詳細な説明については後述します。

注:

「Hello world」サンプルから実際のコンテンツ有効期限ジョブまで、10 種類のタイマー ジョブの例を使用した、より広範な PnP ソリューションについては、「Core.TimerJobs.Samples」を参照してください。

以降の手順で、単純なタイマー ジョブを作成する方法を示します。

手順 1: コンソール プロジェクトを作成し、PnP コアを参照する

「コンソール」タイプの新しいプロジェクトを作成し、次のいずれかの方法で PnP コア ライブラリを参照します。

  • Office 365 Developer パターンおよびプラクティスのコア Nuget パッケージをプロジェクトに追加します。 v15 (オンプレミス) 用と v16 (Office 365) 用の Nuget パッケージがあります。 これは優先オプションです。

  • 既存の PnP コア ソース プロジェクトを自分のプロジェクトに追加します。 これにより、デバッグ時に PnP コア コードにステップインできるようになります。

    注:

    ユーザーは、常にこのコードを PnP に追加された最新の変更内容に更新する必要があります。

手順 2: タイマー ジョブ クラスを作成し、タイマー ジョブ ロジックを追加する

  1. SimpleJob という名前のタイマー ジョブのクラスを追加します。

  2. このクラスに TimerJob 抽象基本クラスを継承させます。

  3. コンストラクターで、タイマー ジョブに名前 (base("SimpleJob")) を付け、TimerJobRun イベント ハンドラーを接続します。

  4. タイマー ジョブ ロジックを TimerJobRun イベント ハンドラーに追加します。

結果は、次のようになります。

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

手順 3: タイマー ジョブを使用するように Program.cs を更新する

前の手順で作成したタイマー ジョブは、実行する必要があります。 これを行うには、次の手順で Program.cs を更新します。

  1. タイマー ジョブ クラスをインスタンス化します。

  2. タイマー ジョブの認証の詳細を提供します。 次の例では、SharePoint Online に対する認証にユーザー名とパスワードを使用します。

  3. タイマー ジョブ プログラムがアクセスする 1 つ以上のサイトを追加します。 この例では、URL にワイルドカード文字を使用します。 タイマー ジョブは、このワイルド カード URL と一致するすべてのサイトで動作します。

  4. 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();
}

タイマー ジョブの展開オプション

前の手順は、単純なタイマー ジョブを示しています。 次の手順では、タイマー ジョブを展開します。

タイマー ジョブは、ホスティング プラットフォーム上でスケジュールを設定する必要がある .exe ファイルです。 選択されたホスティング プラットフォームによって、展開が異なります。 次のセクションでは、2 つの最も一般的なホスティング プラットフォーム オプションについて説明します。

  • ホスティング プラットフォームとして Azure を使用する。
  • ホスティング プラットフォームとして Windows Server を使用する。

Azure WebJobs を使用して Azure にタイマー ジョブを展開する

タイマー ジョブを展開する前に、ユーザーの介入なしにジョブを実行できることを確認します。 この記事のサンプルでは、ユーザーにパスワードまたはクライアント シークレットの入力を求めるメッセージが表示されます (詳細は「認証」を参照してください)。これはテスト時は機能しますが、展開時は機能しません。 すべての既存のサンプルでは、ユーザーに対して、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>

app.config ファイルにこれらの変更を追加したら、Visual Studio からタイマー ジョブを実行し、ユーザーの介入なしで動作することを確認します。

Azure への実際の展開は、Azure WebJobs に基づきます。 このタイマー ジョブの例を展開するには、次の手順を実行します。

  1. Visual Studio でプロジェクトを右クリックし、[Azure WebJob として発行] を選択します。

  2. タイマー ジョブのスケジュールを指定してから、[OK] を選択します

  3. 発行先として [Microsoft Azure Websites] を選択します。 Azure にサインインして、タイマー ジョブをホストする Azure Web サイトを選択するように求められます (必要に応じて新しく作成することもできます)。

  4. [発行] を選択し、WebJob を Azure にプッシュします。

  5. タイマー ジョブが発行されたら、Visual Studio または Azure portal からジョブをトリガーしてその実行を確認できます。

    Azure portal (レガシ)

  6. また、新しい Azure portal からジョブを選択して、[実行] を選択することにより、タイマー ジョブを実行することもできます。 新しいポータルから WebJobs と連動する方法の詳細については、「Azure App Service で WebJobs を使用してバック グラウンド タスクを実行する」の記事を参照してください。

    Azure portal (現在)

注:

Azure WebJob の展開に関する詳細なガイダンスについては、「Office 365 サイト用の Azure WebJobs ("タイマー ジョブ") の使用の開始」を参照してください。

Windows スケジューラを使用して Windows Server にタイマー ジョブを展開する

Windows Server に展開する場合、ユーザーの介入なしにタイマー ジョブを実行する必要があります。

  1. 前のセクション「Azure WebJobs を使用して Azure にタイマー ジョブを展開する」で説明したように app.config ファイルを変更します。

  2. ジョブのリリース バージョンを、実行するサーバーにコピーします。

    重要

    サーバー上に追加のファイルまたはプログラムをインストールしなくてもジョブを実行できるよう、関連するアセンブリ、.exe ファイル、および .config ファイルのすべてをコピーします。

  3. タイマー ジョブの実行をスケジュールします。 組み込みの Windows タスク スケジューラを使用することをお勧めします。 Windows タスク スケジューラを使用するには、次の手順を実行します。

    1. タスク スケジューラを開きます ([コントロール パネル]>[タスク スケジューラ])。
    2. [タスクの作成] を選択し、タスクを実行する名前とアカウントを指定します。
    3. [トリガー] を選択して、新しいトリガーを追加します。 タイマー ジョブのスケジュールを指定します。
    4. [アクション] を選択して、[プログラムの開始] アクションを選択し、タイマー ジョブの .exe ファイルを選択してから、フォルダーで開始を設定します。
    5. [OK] を選択して、タスクを保存します。

Windows タスク スケジューラ

タイマー ジョブ フレームワークの詳細

このセクションでは、タイマー ジョブ フレームワークの機能と動作について詳しく説明します。

構造

TimerJob クラスは、次のパブリック プロパティ、メソッド、およびイベントが含まれている抽象基本クラスです。

TimerJob クラスの構造

ほとんどのプロパティとメソッドについては、以降のセクションで詳しく説明します。 ここでは、それ以外のプロパティとメソッドについて説明します。

  • IsRunning プロパティ: タイマー ジョブが実行中かどうかを示す値を取得します。 実行中の場合は true の値に、実行中でない場合は false の値になります。
  • Name プロパティ: タイマー ジョブの名前を取得します。 名前は、タイマー ジョブのコンストラクターで最初に設定されます。
  • SharePointVersionプロパティ: SharePoint のバージョンを取得または設定します。 このプロパティは、読み込まれている Microsoft.SharePoint.Client.dll のバージョンに基づいて自動的に設定されます。通常は変更しないでください。 ただし、v16 CSOM ライブラリを v15 (オンプレミス) 展開で使用する場合はこのプロパティを変更できます。
  • Version プロパティ: このタイマー ジョブのバージョンを取得します。 バージョンは最初、タイマー ジョブのコンストラクターで設定されるか、コンストラクターで設定されない場合は既定値の 1.0 に設定されます。

タイマー ジョブの実行を準備するには、最初にそれを構成する必要があります。

  1. 認証設定を提供します。
  2. 範囲 (サイトのリスト) を提供します。
  3. オプションとして、タイマー ジョブのプロパティを設定します。

タイマー ジョブの実行が開始されると、実行の観点から次の全体的な手順が実行されます。

  1. サイトを解決する: ワイルド カードのサイト URL (https://tenant.sharepoint.com/sites/d* など) が既存のサイトの実際のリストに解決されます。 サブサイトの展開が要求された場合は、解決済みのサイト リストがすべてのサブサイトを使用して展開されます。
  2. 現在のスレッド設定に基づいて作業バッチを作成し、バッチごとに 1 つのスレッドを作成します。
  3. スレッドが作業バッチを実行し、リスト内の各サイトに対して TimerJobRun イベントを呼び出します。

各手順の詳細については、次のセクションをご覧ください。

認証

タイマー ジョブを使用する前に、タイマー ジョブに SharePoint への認証方法を指示する必要があります。 フレームワークが現在サポートしているのは、AuthenticationType 列挙型、Office365NetworkCredentials、および AppOnly を使用したアプローチです。 また、次のメソッドを使用した場合も、AuthenticationType プロパティが Office365NetworkCredentials、および AppOnly に適切な値に自動的に設定されます。

次のフローチャートでは、実行する手順の後ろに、それぞれのアプローチの詳細な説明が続きます。

認証手順のフローチャート

ユーザー資格情報

Office 365 に対して実行するためのユーザー資格情報を指定するには、次の 2 つのメソッドを使用できます。

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

最初のメソッドは、ユーザー名とパスワードを受け取ります。 2 番目のメソッドでは、Windows 資格情報マネージャーに保存されている汎用の資格情報を指定できます。 次のスクリーンショットは、bertonline 汎用資格情報を示しています。 これを使用してタイマー ジョブを認証するために、2 つ目のメソッドのパラメーターとして bertonline を指定します。

Windows 資格情報マネージャー


SharePoint オンプレミスに対して実行するための同様のメソッドがあります。

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

アプリ専用

アプリ専用の認証は、テナント スコープ アクセス許可を付与できるため、優先メソッドです。 ユーザー資格情報の場合は、ユーザー アカウントに必要なアクセス許可が付与されている必要があります。

注:

特定のサイト解決ロジックがアプリ専用の認証で動作しない場合があります。 詳細は、次のセクションで説明します。

アプリ専用の認証用にジョブを構成するには、次のいずれかのメソッドを使用します。

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

Office 365 または SharePoint オンプレミスのいずれかで同じメソッドを使用し、アプリ専用の認証を使用したタイマー ジョブを環境間で容易に移動することができます。

注:

アプリ専用の認証を使用する場合は、AuthenticationType.AppOnly で動作しない API が使用されていると、タイマー ジョブ ロジックが失敗します。 一般的な例として、検索 API、分類ストアへの書き込み、およびユーザー プロファイル API の使用があります。

実行するサイト

タイマー ジョブを実行する場合は、それを実行する 1 つ以上のサイトが必要です。

タイマー ジョブにサイトを追加する

タイマー ジョブにサイトを追加するには、次のメソッドのセットを使用します。

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

サイトを追加するには、完全修飾 URL (https://tenant.sharepoint.com/sites/dev など) とワイルドカード URL のどちらかを指定します。

ワイルドカード URL は、アスタリスク (*) で終わる URL です。 * は 1 つしか使用できず、URL の最後の文字にする必要があります。 ワイルドカード URL のサンプルが https://tenant.sharepoint.com/sites/* です。この URL は、そのサイトの管理対象パスの下のすべてのサイト コレクションを返します。 別の例として、https://tenant.sharepoint.com/sites/dev* は、URL に dev が含まれるすべてのサイト コレクションを返します。

通常、サイトは、タイマー ジョブ オブジェクトをインスタンス化するプログラムによって追加されますが、必要に応じて、タイマー ジョブで渡されたサイトのリストを制御することもできます。 次のサンプルに示すように、UpdateAddedSites 仮想メソッドのメソッド上書きを追加してこれを実行します。

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

列挙型の資格情報を指定する

ワイルド カード URL を追加して認証をアプリ専用に設定したら、列挙型の資格情報を指定します。 列挙型の資格情報は、実際のサイトのリストを返すサイト照合アルゴリズムで使用されるサイト コレクションのリストを取得するために使用されます。

サイト コレクションのリストを取得するためのタイマー フレームワークの動作は Office 365 (v16) とオンプレミス (v15) で異なります。

  • Office 365: Tenant.GetSiteProperties メソッドが「標準」のサイト コレクションの読み取りに使用され、検索 API が OneDrive for Business サイト コレクションの読み取りに使用されます。
  • SharePoint オンプレミス:すべてのサイト コレクションを読み取るために検索 API が使用されます。

検索 API がユーザー コンテキストと連動しない場合は、タイマー ジョブが指定された列挙型の資格情報にフォールバックします。

Office 365 に対して実行するためのユーザー資格情報を指定するには、次の 2 つのメソッドを使用できます。

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

SharePoint オンプレミスに対して実行するための同様のメソッドがあります。

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

最初のメソッドは、ユーザー名、パスワード、およびオプションでドメイン (オンプレミスの場合) を受け取るだけです。 2 つ目のメソッドは、Windows 資格情報マネージャーに保存されている汎用の資格情報を指定します。 資格情報マネージャーの詳細については、「認証」を参照してください。

サブサイトの展開

多くの場合、タイマー ジョブ コードは、サイト コレクションのルート サイトおよびそのサイト コレクションのすべてのサブサイトに対して実行されます。 これを行うには、ExpandSubSites プロパティを true に設定します。 これにより、サイト解決手順の一部として、タイマー ジョブがサブサイトに展開されます。

解決されたサイトおよび展開されたサイトを上書きする

タイマー フレームワークでワイルド カード サイトが解決され、オプションでサブサイトが展開されたら、次の手順はサイトのリストの処理です。 サイトのリストを処理する前に、サイトのリストの変更が必要となる場合があります。 たとえば、特定のサイトを削除したり、リストにサイトを追加したりする場合があります。 この手順は、ResolveAddedSites 仮想メソッドを上書きすることで実現できます。 次のサンプルは、ResolveAddedSites メソッドを上書きして、リストから 1 つのサイトを削除する方法を示しています。

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

TimerJobRun イベント

タイマー ジョブ フレームワークでは、サイトのリストが作業バッチに分割されます。 サイトの各バッチは、独自のスレッドで実行されます。 既定で、フレームワークによって、5 つのバッチと、それらを実行する 5 つのスレッドが作成されます。 タイマー ジョブ スレッド オプションの詳細については、「スレッド」を参照してください。

スレッドがバッチを処理すると、TimerJobRun イベントがタイマー フレームワークによってトリガーされ、タイマー ジョブを実行するために必要なすべての情報が提供されます。 タイマー ジョブはイベントとして実行されるため、コードでイベント ハンドラーを TimerJobRun イベントに接続する必要があります。

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

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

もう 1 つの方法では、次のようにインライン委任を使用します。

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

TimerJobRun イベントが発生すると、タイマー ジョブ ロジックを記述するために必要な情報を提供する TimerJobRunEventArgs オブジェクトが送られてきます。 このクラスでは、次の属性とメソッドを使用できます。

TimerJobRunEventArgs クラスの構造

いくつかのプロパティとすべてのメソッドが、次のセクションで説明するオプションの状態管理機能で使用されます。 ただし、次のプロパティは、使用されている構成に関係なく、すべてのイベントで常に使用できます。

  • Url プロパティ: 動作対象のタイマー ジョブのサイトの URL を取得または設定します。 サイト コレクションのルート サイトにすることができますが、サイトの展開が行われた場合は、サブサイトにすることもできます。
  • ConfigurationData プロパティ: 追加のタイマー ジョブ構成データを取得または設定します (オプション)。 この構成データは、TimerJobRunEventArgs オブジェクトの一部として渡されます。
  • WebClientContext プロパティ: 現在の URL の ClientContext オブジェクトを取得または設定します。 このプロパティは、Url プロパティで定義されたサイトの ClientContext オブジェクトです。 これは、通常、タイマー ジョブ コードで使用される ClientContext オブジェクトです。
  • SiteClientContext プロパティ: サイト コレクションのルート サイトの ClientContext オブジェクトを取得または設定します。 このプロパティは、タイマー ジョブがアクセスするために必要なルート サイトへのアクセス権を提供します。 たとえば、タイマー ジョブは、SiteClientContext プロパティを使用してページ レイアウトをマスター ページ ギャラリーに追加できます。
  • TenantClientContext プロパティ: テナント API と連動する ClientContext オブジェクトを取得または設定します。 このプロパティは、テナント管理サイトの URL を使用して構築された ClientContext オブジェクトを提供します。 タイマー ジョブの TimerJobRun イベント ハンドラーでテナント API を使用するには、この TenantClientContext プロパティを使用して新しい Tenant オブジェクトを作成します。

すべての ClientContext オブジェクトが、「認証」に記載されている認証情報を使用します。 ユーザー資格情報を選択した場合は、ユーザー アカウントが、指定されたサイトに対して動作するために必要なアクセス許可を持っていることを確認します。 アプリ専用を使用している場合は、テナント スコープ アクセス許可をアプリ専用プリンシパルに設定することをお勧めします。

状態管理

タイマー ジョブ ロジックを作成する場合は、状態を持続させる必要があります。たとえば、サイトが最後に処理された時点を記録するためやタイマー ジョブのビジネス ロジックをサポートするデータを保存するためなどです。 このために、タイマー ジョブ フレームワークは状態管理機能を備えています。

状態管理では、標準およびカスタムのプロパティ セットを、処理されたサイトの Web プロパティ バッグの JSON シリアル化された文字列 (名前 = タイマー ジョブの名前 + 「_Properties」) として保存および取得します。 TimerJobRunEventArgs オブジェクトの既定のプロパティを以下に示します。

  • PreviousRun プロパティ: 前回の実行の日付と時刻を取得または設定します。
  • PreviousRunSuccessful プロパティ: 前回の実行が正常に完了したかどうかを示す値を取得または設定します。 タイマー ジョブの作成者は、タイマー ジョブの実装の一部として CurrentRunSuccessful プロパティを設定することにより、ジョブの実行に成功というフラグを付ける必要があることに注意してください。
  • PreviousRunVersion プロパティ: 前回の実行のタイマー ジョブ バージョンを取得または設定します。

これらの標準プロパティの横には、TimerJobRunEventArgs オブジェクトの Properties コレクションにキーワード (keyword)値ペアを追加することで、独自のプロパティを指定するオプションもあります。 これを容易にするために、次の 3 つのメソッドがあります。

  • SetProperty はプロパティを追加または更新します。
  • GetProperty はプロパティの値を返します。
  • DeleteProperty は、プロパティ コレクションからプロパティを削除します。

次のコードは、状態管理の使用方法を示しています。

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

状態は、1 つの JSON シリアル化プロパティとして保存されます。これは、他のカスタマイズにも使用できることを意味します。 たとえば、タイマー ジョブが状態エントリ「SiteCompliant = false」を書き込んだ場合は、タイマー ジョブが、サイトが準拠していないと判断したことになるため、JavaScript ルーチンでユーザーのアクションを求めることができます。

スレッド

タイマー ジョブ フレームワークは、既定で、スレッドを使用して作業を並列化します。 スレッドは、サブサイトの展開 (要求された場合) と、各サイトに対する実際のタイマー ジョブ ロジックの実行 (TimerJobRun イベント) の両方に使用されます。 スレッドの実装を制御するには、次のプロパティを使用できます。

  • UseThreading プロパティ: スレッドを使用するかどうかを示す値を取得または設定します。 既定は true です。 メイン アプリケーション スレッドを使用してすべてのアクションを実行するには、false に設定します。
  • MaximumThreads プロパティ: このタイマー ジョブに使用するスレッドの数を取得または設定します。 有効な値は 2 から 100 です。 既定は 5 です。 必ずしも多数のスレッドを使用する方が少数のスレッドを使用するより高速になるとは限りません。 最適な数は、さまざまなスレッド数を使用したテストを通して特定する必要があります。 既定の 5 スレッドは、ほとんどのシナリオでパフォーマンスを大幅に向上することがわかっています。

調整

タイマー ジョブはスレッドを使用し、大抵のタイマー ジョブの操作がリソース消費型であるため、タイマー ジョブの実行を調整することができます。 調整を正しく処理するために、タイマー ジョブ フレームワークと PnP コアの全体で、既定の ExecuteQuery メソッドではなく、ExecuteQueryRetry メソッドが使用されます。

注:

タイマー ジョブの実装コードで ExecuteQueryRetry を使用することが重要です。

同時実行の問題 - 同じスレッド内のサイト コレクションのすべてのサブサイトを処理する

複数のスレッドを使用してサブサイトを処理すると、タイマー ジョブで同時実行の問題が発生する可能性があります。

一例を挙げます。スレッド A はサイト コレクション 1 の最初のサブサイトのセットを処理し、スレッド B はサイト コレクション 1 の残りのサブサイトを処理します。 タイマー ジョブがサブサイトとルート サイトを処理する (SiteClientContext オブジェクトを使用して) 場合は、スレッド A とスレッド B の両方がルート サイトを処理するため、同時実行の問題が発生する可能性があります。

同時実行の問題を回避する (1 つのスレッド内でタイマー ジョブを実行せずに) には、タイマー ジョブ内で GetAllSubSites メソッドを使用します。

次のコードは、タイマー ジョブ内で GetAllSubSites メソッドを使用する方法を示しています。

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

ログ

タイマー ジョブ フレームワークは、PnP コア ライブラリの一部であるため、PnP コア ログ コンポーネントを使用します。 組み込みの PnP コア ログをアクティブにするには、該当する構成ファイル (app.config または web.config) を使用して構成します。 次の例は、必要な構文を示しています。

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

上の構成ファイルを使用すると、タイマー ジョブ フレームワークは System.Diagnostics.TextWriterTraceListener を使用して、タイマー ジョブの .exe と同じフォルダー内の trace.log ファイルにログを書き込みます。 利用可能なその他のトレース リスナーには、次のようなものがあります。

タイマー ジョブ フレームワークと同じログ アプローチを、カスタムのタイマー ジョブ コードに使用することを強くお勧めします。 タイマー ジョブ コード内で、PnP コア 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);
    }
}

関連項目