進行状況の追跡を始めよう
Trailhead のホーム
Trailhead のホーム

実行コンテキストの理解

学習の目的

この単元を完了すると、次のことができるようになります。

  • Apex の呼び出しに使用する方法を理解する
  • Salesforce オブジェクトのトリガを記述する
  • 開発者コンソールでコードを実行して実行コンテキストの動作を観察する
  • ガバナ制限が設計パターンに与える影響を理解する
  • 一括操作を使用することの重要性を理解する

実行コンテキストとは?

ASP.NET アプリケーションの場合、コードはアプリケーションドメインのコンテキストで実行されます。Lightning プラットフォーム環境では、コードは実行コンテキスト内で実行されます。端的に言うと、このコンテキストはコードが実行されてから終了するまでの時間を表します。重要なのは、作成する Apex コード以外のコードが実行される可能性があると理解しておくことです。

このしくみを理解するには、プラットフォームで Apex コードを実行できる方法をすべて知っておく必要があります。

Apex を呼び出す方法
方法 説明
データベーストリガ カスタムまたは標準オブジェクトでの特定のイベントに対して呼び出されます。
匿名 Apex 開発者コンソールやその他のツールにおいてその場で実行されるコードスニペット。
非同期 Apex future または Queueable Apex の実行時、一括処理ジョブの実行時、または指定間隔での Apex 実行のスケジュール時に行われます。
Web サービス SOAP または REST Web サービスを介して公開されるコード。
メールサービス 受信メールを処理するために設定されるコード。
Visualforce または Lightning ページ Visualforce コントローラおよび Lightning コンポーネントは、自動的に、またはユーザがアクションを開始したとき (ボタンのクリックなど) に Apex コードを実行できます。Lightning コンポーネントも Lightning プロセスおよびフローによって実行できます。

Apex コードの呼び出し以外にも、新規 ToDo の作成、メールの送信、項目自動更新の実行、アウトバウンドメッセージの送信などのアクションすべてを、いずれかの宣言型プラットフォーム機能によってトリガできます。これらのアクションも、実行コンテキスト内で実行されます。

もう 1 つの重要な考慮事項は、Apex コードを実行するユーザのコンテキストです。デフォルトでは、Apex はシステムコンテキストで実行されます。Apex コードはすべてのオブジェクトおよび項目にアクセスできます。オブジェクト権限、項目レベルセキュリティ、共有ルールは現在のユーザには適用されません。with sharing キーワードを使用して、クラスで現在のユーザの共有ルールを考慮するように指定できます。このトピックは重要であるため、詳細は、「with sharing または without sharing キーワードの使用」を参照してください。

トリガの基礎

実行コンテキストについて詳しく学習する前に、少し立ち止まってデータベーストリガについて説明しましょう。SQL Server のトリガと同様に、Apex データベーストリガは Salesforce のレコードに対するイベントの前または後にプログラミングロジックを実行します。トリガを定義するときに、次のイベントの 1 つ以上を指定できます。

  • before insert
  • before update
  • before delete
  • after insert
  • after update
  • after delete
  • after undelete

トリガの基本構文は次のようになります。

trigger TriggerName on ObjectName (trigger_events) {
   // code_block
}

.NET 開発者がコードで問題を解決するのが好きなことはわかっていますが、ここでは生産性を飛躍的に高めるヒントを紹介します。トリガは、同じ処理を Salesforce が提供するポイント & クリック自動化ツールのいずれでも実行できないことが絶対確実である場合にのみ、最後の手段として使用します。

効率的に作業を進めるために、Salesforce プラットフォームには、Lightning プロセスビルダーや Lightning Flow Builder など、コードを記述せずにビジネスロジックを管理するための自動化ツールが用意されています。ほとんどの場合、かつてはトリガでしか実行できなかったタスクに、より適した自動化ツールがあります。

メモ

メモ

初めてこのプラットフォームを使用する場合は、トリガを作成する前に、必ず時間を取って開発者向け初級トレイルの「Lightning フロー」モジュールを参照してください。Salesforce 組織に多大かつ不要な技術的作業負担をもたらした張本人になることを避けましょう。

実行コンテキストのマーク

実行コンテキストの理解を深めるために、新規取引先が入力されたら商談を作成する Apex データベーストリガを手順に従って作成してみましょう。このトリガはハンドラクラスからメソッドをコールするため、まずハンドラクラスを作成します。

  1. [設定] で、[]あなたの名前> > [開発者コンソール] をクリックして開発者コンソールを開きます。
  2. 開発者コンソールで、[File (ファイル)] > [New (新規)] > [Apex Class (Apex クラス)] を選択します。
  3. クラス名として「AccountHandler」と入力し、\[OK\] をクリックします。
  4. 既存のコードを削除して、次のスニペットを挿入します。

    public with sharing class AccountHandler {
    
        public static void CreateNewOpportunity(List<Account> accts) {
    
            for (Account a : accts) {
                Opportunity opp = new Opportunity();
                opp.Name = a.Name + ' Opportunity';
                opp.AccountId = a.Id;
                opp.StageName = 'Prospecting';
                opp.CloseDate = System.Today().addMonths(1);
                insert opp;
            }
        }
    
    }
    
  5. Ctrl + S キーを押してクラスを保存します。

メモ

メモ

次の例のように、オブジェクトごとにトリガを 1 つのみ使用するのがベストプラクティスと考えられています。そうすることで、トリガ内でコンテキスト固有のハンドラメソッドを使用してロジックのないトリガを作成できます。この方法を採用することで、新しい開発者がよく陥る落とし穴を避けることができます。これらの技法についての詳細は、「Trigger Frameworks and Apex Trigger Best Practices (トリガのフレームワークと Apex トリガのベストプラクティス)」を参照してください。

ハンドラクラスができたので、取引先トリガを作成します。

  1. 開発者コンソールで、[File (ファイル)] > [New (新規)] > [Apex Trigger (Apex トリガ)] を選択します。
  2. 名前に「AccountTrigger」と入力し、sObject として \[Account (取引先)\] を選択します。
  3. [送信] をクリックします。
  4. 既存のコードを削除して、次のスニペットを挿入します。

    trigger AccountTrigger on Account (before insert, before update, before
        delete, after insert, after update, after delete,  after undelete) {
    
        if (Trigger.isAfter && Trigger.isInsert) {
            AccountHandler.CreateNewOpportunity(Trigger.New);
        }
    }
    
  5. Ctrl + S キーを押してトリガを保存します。

手順の最後に、Salesforce インターフェースを使用して新規取引先を入力するユーザをシミュレートする匿名コードを実行します。Apex コードを実行するにはさまざまな方法があります。

  1. [設定] で、[]あなたの名前> > [開発者コンソール] をクリックして開発者コンソールを開きます。
  2. [Debug (デバッグ)] > [Open Execute Anonymous Window (実行匿名ウィンドウを開く)] を選択します。
  3. 既存のコードを削除して、次のスニペットを挿入します。

    Account acct = new Account(
        Name='Test Account 2',
        Phone='(415)555-8989',
        NumberOfEmployees=50,
        BillingCity='San Francisco');
    insert acct;
    
  4. \[Open Log (ログを開く)\] オプションが選択されていることを確認し、\[Execute (実行)\] をクリックします。新しいタブに実行ログが表示されます。実行ログを入念に調査できるように開いたままにします。

実行ログの調査

実行ログの最初の行には EXECUTION_STARTED イベント、最後の行には EXECUTION_FINISHED イベントがマークされています。その間にあるすべてが実行コンテキストです。

では、何が起こったかを詳しく見ていきましょう。CODE_UNIT_STARTED イベントは、\[Execute Anonymous (実行匿名)\] ウィンドウからコードが起動されたときにマークされます。下図では、この行は赤で強調表示されています。

開発者コンソールのデバッグログのスクリーンショット。CODE_UNIT_STARTED イベントが強調表示されています。

強調表示された 2 つ目の CODE_UNIT_STARTED 行は、BeforeInsert イベントのコードが実行されたときを表します。

この図には表示されていませんが、開発者コンソールの独自のインスタンスを使用している場合は、結果をさらにスクロールダウンして CODE_UNIT_STARTED の他のインスタンスを探してください。少なくとももう 1 つ、AfterInsert イベントのコードが実行されたときを表すインスタンスが表示されるはずです。新規取引先が作成されたら起動するワークフロールールを作成した場合は、それらも実行ログに表示されます。このコードはすべて同じ実行コンテキスト下で動作するため、同じガバナ制限セットが適用されます。

なぜガバナ制限を理解するのがそれほど重要なのでしょうか? Salesforce はマルチテナント環境であるため、1 つの Salesforce 組織の各インスタンスがリソースを消費しすぎないようにこうしたガバナ制限が不可欠です。基本的に、ガバナ制限はシステム全体がクラッシュしないようにするものです。

制限への対応

再び、制限への対応というテーマに戻ってきました。おそらく最も考慮することになる 2 つの制限には、SOQL クエリまたは DML ステートメントの数が関係します。このプラットフォームに不慣れな開発者はこれらの制限に足をすくわれがちなので、少し余分に時間をとって回避方法に取り組んでください。

メモ

メモ

意識すべき制限は数多くあり、それらはメジャーリリースごとに変化する傾向にあります。さらに、制限が厳しくならずに緩くなることもよくあるため、必ず「リソース」にある「実行ガバナと制限」リンクで最新の制限を確認してください。

一括処理

多くの開発者が、コードを 1 件のレコードを処理するように設計するという共通の罠に落ちています。Lightning プラットフォームではこれは大きな間違いであることにすぐ気付くでしょう。

Apex トリガが一度に受信できるオブジェクトは最大 200 個です。現在、SOQL クエリの合計数の同期制限は 100、発行される DML ステートメントの合計数の同期制限は 150 です。そのため、ループ内で SOQL クエリまたは DML ステートメントを実行するトリガがあり、そのトリガが一括処理で起動されたらどうなるでしょうか?

そうです!

制限エラーになります。コードをリリースしてしばらくの間、つまり制限に達するまでは正常に実行できる可能性があります。ひとたび制限に達したら、開発者はすぐにコードを調べて「一括処理対応」方法を解明しなければなりません。この設計し直しに最初の設計よりも時間がかかることがよくあります。この状況を避けるために、最初から Apex コードを一括処理に対応できるように設計します。この方法は、「一括 Apex トリガ」モジュールで学習できます。

お気づきかもしれませんが、前に作成したトリガハンドラコードでは一括パターンを使用していなかったため、制限エラーになる可能性があります。もう一度、元のコードを確認してみましょう。

public with sharing class AccountHandler {

    public static void CreateNewOpportunity(List<Account> accts) {

        for (Account a : accts) {
            Opportunity opp = new Opportunity();
            opp.Name = a.Name + ' Opportunity';
            opp.AccountId = a.Id;
            opp.StageName = 'Prospecting';
            opp.CloseDate = System.Today().addMonths(1);
            insert opp;
        }
    }

}

insert DML 操作が for ループ内にあります。これは、非常によくない方法ですので、常に避けてください。

幸い、1 ステップでループ内のリスト変数に書き出し、リストの内容を挿入するようにコードを変更して修正できます。

  1. [設定] で、[]あなたの名前> > [開発者コンソール] をクリックして開発者コンソールを開きます。
  2. 開発者コンソールで [File (ファイル)] > [Open (開く)] を選択します。
  3. エンティティ種別として、\[Classes (クラス)\] を選択します。エンティティとして \[AccountHandler\] を選択します。
  4. \[Open (開く)\] をクリックします。
  5. 既存のコードを削除して、次のスニペットを挿入します。

    public with sharing class AccountHandler {
    
        public static void CreateNewOpportunity(List<Account> accts) {
    
            List<Opportunity> opps = new List<Opportunity>();
    
            for (Account a : accts) {
                Opportunity opp = new Opportunity();
                opp.Name = a.Name + ' Opportunity';
                opp.AccountId = a.Id;
                opp.StageName = 'Prospecting';
                opp.CloseDate = System.Today().addMonths(1);
                opps.add(opp);
            }
    
            if (opps.size() > 0) {
                insert opps;
            }
        }
    
    }
    
  6. Ctrl + S キーを押してクラスを保存します。

トリガハンドラコードを修正できたので、テストしてトリガが 200 レコードという負荷を処理できることを確認しましょう。ご存じのように、ベストプラクティスとして、コードが機能することを確認できるように単体テストを記述します。

  1. 開発者コンソールで、[File (ファイル)] > [New (新規)] > [Apex Class (Apex クラス)] を選択します。
  2. クラス名として「AccountTrigger_Test」と入力し、\[OK\] をクリックします。
  3. 既存のコードを削除して、次のスニペットを挿入します。

    @isTest
    private class AccountTrigger_Test {
    
        @isTest static void TestCreateNewAccountInBulk() {
    
            // Test Setup data
            // Create 200 new Accounts
            List<Account> accts = new List<Account>();
            for(Integer i=0; i < 200; i++) {
                Account acct = new Account(Name='Test Account ' + i);
                accts.add(acct);
            }              
    
            // Perform Test
            Test.startTest();
            insert accts;                               
            Test.stopTest();
    
            // Verify that 200 new Accounts were inserted
            List<Account> verifyAccts = [SELECT Id FROM Account];
            System.assertEquals(200, verifyAccts.size());    
    
            // Also verify that 200 new Opportunities were inserted
            List<Opportunity> verifyOpps = [SELECT Id FROM Opportunity];                              
            System.assertEquals(200, verifyOpps.size());                             
        }
    }
    
  4. Ctrl + S キーを押してクラスを保存します。

  5. [Test (テスト)] > [New Run (新規実行)] を選択します。

  6. TestClass として AccountTrigger_Test、テストメソッドとして TestCreateNewAccountInBulk を選択します。

  7. [実行] をクリックします。

  8. \[Test (テスト)\] タブを選択し、テストがエラーなしで完了したことを確認します。これは、\[Status (状況)\] 列の緑のチェックマークで示されます。

メモ

メモ

単体テストについてはまだ説明していませんが、心配はいりません。いくつか異なるキーワードがあるだけで、Lightning プラットフォームでも .NET と同様に動作します。その構造についてはすぐに習熟できます。Apex トリガのテストについての詳細は、「リソース」のリンクを参照してください。

もうひとこと...

Apex では、お馴染みの try-catch-finally ブロックを使用して例外を処理します。ただし、Apex コードが実行される場所に応じて、catch ステートメントとおそらく rollback も変わる可能性があります。Apex で try、catch、rollback を処理する際のベストプラクティスについては、「リソース」のリンクを参照してください。

Lightning プラットフォームにはアプリケーション変数やセッション変数のようなものはありません。クラス間でデータを保持する必要がある場合は、静的変数を使用します。ただし、静的変数の動作は Lightning プラットフォームと .NET では異なることに留意してください。Lightning プラットフォーム環境では、静的変数が情報を保持できるのは 1 つの実行コンテキスト内に限定されますが、他のオプションを使用してトリガ呼び出し間でデータを保持することはできます。詳細は、「リソース」の高度な Apex に関するリンクを参照してください。

制限に対応する場合、特に管理パッケージの開発者は、多くのトレードオフを考慮する必要があります。Salesforce パートナーは通常、管理パッケージを使用してアプリケーションを配布および販売します。このモジュールでは、知っておくべき情報の概要をざっと確認しただけです。Apex 開発に本格的に取り組む場合は、「リソース」の高度な Apex に関する リンクを参照してください。

リソース