Upsert を使用してレコードを作成または更新

Upsert メッセージを使用して、データ統合のシナリオに含まれる複雑さを減らすことができます。 データを外部システムから Microsoft Dataverse に読み込むとき、大量データの統合の場合など、レコードが Dataverse に既に存在するかが分からない場合があります。 このような場合、Update または Create メッセージを使用する必要があるかどうかが分かりません。 適切な操作を実行する前に、そのレコードが存在するかどうかを決定するために、最初にそのレコードを取得する必要があります。 Upsert メッセージを使用すると、この複雑さを軽減してデータをより効率的に Dataverse に読み込むことができます。

Upsert の使用は、Create の使用と比べると、パフォーマンスを低下させます。 レコードが存在しないことが確実な場合は、Create を使用します。

注意

Upsert で主キーの値を使用できますが、一般的な使用例はデータ統合シナリオであるため、通常は代替キーを使用することが予想されます。 詳細情報: レコードを参照する代替キーの使用

エラスティック テーブルの upsert

Upsert のエラスティック テーブルの動作は、標準テーブルとは異なります。 エラスティック テーブルでは、レコードが既に存在するかどうかに応じて、Upsert 操作は CreateUpdate メッセージを呼び出しません。 Upsert は、エンティティ内の変更を直接適用します。

  • レコードが存在する場合: レコード内のすべてのデータがエンティティ内のデータによって上書きされます。 Update イベントはありません。
  • レコードが存在しない場合: 新しいレコードが作成されます。 Create イベントはありません。

これは、イベントのビジネス ロジックを適用する場所に影響します。 新しいレコードは、CreateUpsert を使用して作成できます。 レコードは UpdateUpsert のいずれかを使用して更新できます。 エラスティック テーブルの CreateUpdate に一貫してロジックを適用する必要がある場合は、そのロジックを Upsert にも含める必要があります。 詳細情報: エラスティック テーブルにレコードを upsert する

標準テーブルの upsert プロセスを理解する

Upsert メッセージはサーバーで処理されます。 SDK for .NET クラスは、サーバーで使用されるものと同じオブジェクトを使用します。 したがって、次の説明では、.NET 用 SDK クラスを使用して、UpsertRequest インスタンスがどのように処理され、 UpsertResponse インスタンスが返されるかを説明します。

次の手順では、標準テーブル用に UpsertRequest を受け取ったときの、サーバーでの処理ロジックについて説明します。

  1. UpsertRequest インスタンスは、Create または Update オペレーションの操作のデータを含む Entity インスタンスで設定された Target Property で届きます。
    • 通常、Entity インスタンスには、代替キーを使用してレコードを識別するために使用される値が設定された Entity.KeyAttributes Property があります。
  2. 存在する場合、Dataverse は、Target プロパティに設定された Entity インスタンスの Entity.Id プロパティを使用してレコードを検索しようとします。 それ以外の場合は、Entity.KeyAttributes Property の代替キー値が使用されます。
  3. レコードが存在する場合は、次の手順を実行します。
    1. Target Entity.Id に、見つかったレコードの主キーの値を設定します。
    2. Target Entity.KeyAttributes コレクションで使用しているのと同じキーを使用する Target Entity.Attributes コレクションのデータをすべて削除します。
    3. Update を呼び出します。
    4. UpsertResponse.RecordCreated プロパティを false に設定します。
    5. UpsertResponse.Target の値として、Target エンティティから EntityReference を作成します。
    6. UpsertResponse を返します。
  4. レコードが存在しない場合は、次の手順を実行します。
    1. Target Entity.Attributes コレクションに持たない Target Entity.KeyAttributes のデータを Target Entity.Attributes にすべてコピーします。
    2. Create を呼び出します。
    3. UpsertResponse.RecordCreatedtrue に設定します。
    4. EntityReferenceTarget エンティティから作成し、Create 操作の id 結果を UpsertResponse.Target の値として作成します。
    5. UpsertResponse を返します。

次の図は、UpsertRequest を受け取ったときのサーバー上のプロセスを示しています。

upsert プロセス フロー

リクエスト作成のガイダンス

代替キーを使用してレコードを識別する場合、保存するデータを表す要求の一部に代替キー データを含めることは推奨されず、必要もありません。

Web API を使用していて .NET 用 SDK に慣れていない場合は、上記のサーバー側のプロセスに従うのを困難に感じる場合があります。 Web API には、説明と上の図で使用されている SDK オブジェクトと同じオブジェクト モデルはありませんが、下の表に示すようにデータをマッピングできます。

Web API SDK 説明設定
URL のキー値 Entity.KeyAttributes Property レコードを識別する代替キー データが含まれています。
要求の本文 EntityUpsertRequest.Target Property に設定 Create または Update に使用するデータが含まれています。

これらの要求は上記のようにサーバー上で処理されますが、次のように考えることができます。

  • レコードが存在する場合: URL 内の代替キー値の要求本文のデータ セットは 削除 されるため、含める意味がありません。 これにより、代替キー値を使用してレコードを識別しているときに、レコードの代替キー値を更新できなくなります。 代替キーの値は、主キーまたは別の代替キー セットを使用して変更できます。
  • レコードが存在しない場合: URL の代替キーに指定された値と_データが違っていても_、要求本文に設定されている代替キー値が新しいレコードの作成に使用されます。 要求本文に代替キー データがない場合は、URL の代替キー データが要求本文にコピーされます。 URL 内のキー値と対応する本文のキー値が一致しない状況を回避するには、その値を本文にまったく含めないことが最善策です。

Web API の使用

Web API を使用すると、UpsertUpdate の両方のメッセージが、URL のキーによって識別される指定された EntitySet リソースに対して http PATCH を使用して開始されます。

UpsertUpdate の違いは、If-Match: * 要求ヘッダーが含まれているかどうかによって定義されます。 If-Match: * 要求ヘッダーが含まれており、URL 内のキー値に一致するリソースがない場合、要求は 404 Not Found 状態コードを返します。 If-Match: * 要求ヘッダーは、PATCH 要求が Update 操作であることを保証します。

If-Match: * 要求ヘッダーが含まれていない場合、PATCH 要求は Upsert のように扱われ、URL のキーに一致するレコードが見つからない場合は新しいレコードが作成されます。 ただし、SDK とは異なり、応答からレコードが作成されたかどうかはわかりません。 状態の応答は、どちらの場合も 204 No Content です。

Prefer: return=representation 要求ヘッダーを含める場合、Create には 201 Created の状態を返し、Update には 200 OK の状態を返します。 このヘッダーを追加すると、追加の Retrieve 操作が追加されるため、パフォーマンスに影響します。 このオプションを使用する場合は、追加する $select クエリ オプションに主キー値のみが含まれていることを確認してください。 詳細情報:

PATCH 要求では、If-None-Match: * 要求ヘッダーを含めて、レコードを作成するだけの場合は Update をブロックすることもできます。 詳細: upsert 操作の制限

Web API サンプル コード

次の例は、2 つの代替キー列を持つテーブルを使用した Upsert 操作を示しています。

Upsert で作成

この要求により、レコードが作成されます。

要求:

PATCH [Organization Uri]/api/data/v9.2/example_records(example_key1=2,example_key2=2) HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json
Content-Type: application/json

{ "example_name": "2:2" }

応答:

HTTP/1.1 204 No Content
OData-Version: 4.0
OData-EntityId: [Organization Uri]/api/data/v9.2/example_records(example_key1=2,example_key2=2)

Upsert で更新

この要求は、上記のリクエスト要求によって作成されたレコードを更新します。

要求:

PATCH [Organization Uri]/api/data/v9.2/example_records(example_key1=2,example_key2=2) HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json
Content-Type: application/json

{ "example_name": "2:2 Updated" }

応答:

HTTP/1.1 204 No Content
OData-Version: 4.0
OData-EntityId: [Organization Uri]/api/data/v9.2/example_records(example_key1=2,example_key2=2)

注意

応答は、作成または更新操作で同じです。

Upsert と return=representation 設定を使用して作成する

Prefer: return=representation ヘッダーを使用すると、応答で別の状態コードを取得して、レコードが作成されたか更新されたかを示すことができます。

次の要求は、新しいレコードを作成し、状態 201 Created を返します。

要求:

PATCH [Organization Uri]/api/data/v9.2/example_records(example_key1=3,example_key2=3)?$select=example_recordid HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json
Prefer: return=representation
Content-Type: application/json

{ "example_name": "3:3" }

応答:

HTTP/1.1 201 Created
Content-Type: application/json; odata.metadata=minimal
ETag: W/"71004878"
Preference-Applied: return=representation
OData-Version: 4.0

{
  "@odata.context": "[Organization Uri]/api/data/v9.2/$metadata#example_records(example_recordid)/$entity",
  "@odata.etag": "W/\"71004878\"",
  "example_recordid": "ef0d112e-d70e-ed11-82e5-00224822577b"
}

Upsert および return=representation の基本設定で更新

この要求は、上記の要求によって作成されたレコードを更新し、状態 200 OK を返して、これが更新操作であったことを示します。

要求:

PATCH [Organization Uri]/api/data/v9.2/example_records(example_key1=3,example_key2=3)?$select=example_recordid HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json
Prefer: return=representation
Content-Type: application/json

{ "example_name": "3:3 Updated" }

応答:

HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
ETag: W/"71004880"
OData-Version: 4.0

{
  "@odata.context": "[Organization Uri]/api/data/v9.2/$metadata#example_records(example_recordid)/$entity",
  "@odata.etag": "W/\"71004880\"",
  "example_recordid": "ef0d112e-d70e-ed11-82e5-00224822577b"
}

SDK for .NET の使用

クライアント アプリケーションは、CreateUpdate 操作データがある Entity インスタンスで設定された Target Propertyを持つ UpsertRequest インスタンスを含む IOrganizationService.Execute Method を使用しています。 通常、Entity インスタンスには、代替キーを使用してレコードを識別するために使用される値が設定された Entity.KeyAttributes Property があります。

UpsertResponse.RecordCreated Property は、レコードが作成されたかどうかを示し、UpsertResponse.Target には、作成または更新されたレコードへの参照が含まれています。

SDK for .NET サンプル コード

Upsert でレコードを挿入する サンプルの SampleMethod.cs ファイルは、次の ProcessUpsert メソッドを含み、XML ファイルのコンテンツに UpsertRequest メッセージを適用して、新しいレコードの作成や既存レコードの更新を実行します。

public static void ProcessUpsert(CrmServiceClient service, String Filename)
{
    Console.WriteLine("Executing upsert operation.....");
    XmlTextReader tr = new XmlTextReader(Filename);
    XmlDocument xdoc = new XmlDocument();
    xdoc.Load(tr);
    XmlNodeList xnlNodes = xdoc.DocumentElement.SelectNodes("/products/product");

    foreach (XmlNode xndNode in xnlNodes)
    {
        String productCode = xndNode.SelectSingleNode("Code").InnerText;
        String productName = xndNode.SelectSingleNode("Name").InnerText;
        String productCategory = xndNode.SelectSingleNode("Category").InnerText;
        String productMake = xndNode.SelectSingleNode("Make").InnerText;

        //use alternate key for product
        Entity productToCreate = new Entity("sample_product", "sample_productcode", productCode);

        productToCreate["sample_name"] = productName;
        productToCreate["sample_category"] = productCategory;
        productToCreate["sample_make"] = productMake;
        var request = new UpsertRequest()
        {
            Target = productToCreate
        };

        try
        {
            // Execute UpsertRequest and obtain UpsertResponse.
            var response = (UpsertResponse)service.Execute(request);
            if (response.RecordCreated)
                Console.WriteLine("New record {0} is created!", productName);
            else
                Console.WriteLine("Existing record {0} is updated!", productName);
        }

        // Catch any service fault exceptions that Dataverse throws.
        catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
        {
            throw;
        }
    }
}

関連項目

変更の追跡を使用してデータを外部システムに同期
テーブルの代替キーを定義する
代替キーを使用してレコードを参照

注意

ドキュメントの言語設定についてお聞かせください。 簡単な調査を行います。 (この調査は英語です)

この調査には約 7 分かかります。 個人データは収集されません (プライバシー ステートメント)。