Upsert メッセージを使用すると、データ統合シナリオの複雑さを軽減できます。 一括データ統合シナリオなど、外部システムから Microsoft Dataverse にデータを読み込む場合、Dataverse にレコードが既に存在するかどうかがわかりません。 このような場合、 Update または Create メッセージのどちらを使用するかを決定することはできません。 適切な操作を実行する前に、レコードが存在するかどうかを確認するために、まずレコードを取得する必要があります。
Upsert メッセージを使用すると、この複雑さを軽減してデータをより効率的に Dataverse に読み込むことができます。
UpsertではなくCreateを使用すると、パフォーマンスが低下します。 レコードが存在しないことが確実な場合は、Create を使用します。
ヒント
主キー値は Upsertで使用できますが、一般的なユース ケースはデータ統合シナリオであるため、代替キーを使用することが一般的に予想されます。 詳細については、「 代替キーを使用してレコードを参照する」を参照してください。
エラスティック テーブルのアップサート
Upsertのエラスティック テーブルの動作は、標準テーブルとは異なります。 エラスティック テーブルを使用すると、 Upsert 操作では、レコードが既に存在するかどうかに応じて、 Create または Update メッセージは呼び出されません。
Upsert は、エンティティ内の変更を直接適用します。
-
レコードが存在する場合: 操作は、レコード内のすべてのデータをエンティティ内のデータで上書きします。
Updateイベントはありません。 -
レコードが存在しない場合: 操作によって新しいレコードが作成されます。
Createイベントはありません。
この動作は、イベントにビジネス ロジックを適用する場所に影響します。
CreateまたはUpsertを使用して、新しいレコードを作成できます。 レコードは、 Update または Upsertを使用して更新できます。 エラスティック テーブルの Create や Update に一貫してロジックを適用する必要がある場合は、そのロジックを Upsert にも含める必要があります。 詳細については、 エラスティック テーブルのレコードのアップサートを参照してください。
標準テーブルのアップサート プロセスを理解する
サーバーは upsert メッセージを処理します。 SDK for .NET クラスでは、サーバーと同じオブジェクトが使用されます。 したがって、次の説明では、SDK for .NET クラスを使用して、サーバーが UpsertRequest クラス インスタンスを処理し、 UpsertResponse クラス インスタンスを返す方法について説明します。
次の手順では、標準テーブルの UpsertRequest を受信したときのサーバー上の処理ロジックについて説明します。
-
UpsertRequest インスタンスは、または操作のデータを含む
CreateインスタンスにUpdateが設定された状態で到着します。- 通常、Entity インスタンスには、代替キーを使用してレコードを識別する値が設定された Entity.KeyAttributes プロパティがあります。
- 存在する場合、Dataverse は、Target プロパティに設定された Entity インスタンスの Entity.Id プロパティを使用してレコードを検索しようとします。 それ以外の場合は、 Entity.KeyAttributes プロパティの代替キー値を使用します。
-
レコードが存在する場合は、次の手順を実行します。
-
TargetEntity.Id を見つかったレコードの主キー値に設定します。 -
TargetEntity.KeyAttributes コレクションと同じキーを使用するデータをTargetEntity.Attributes コレクションから削除します。 -
Updateを呼び出します。 -
UpsertResponse.RecordCreated プロパティを
falseに設定します。 -
UpsertResponse.Target の値として、
Targetエンティティから EntityReference を作成します。 - UpsertResponse を返します。
-
-
レコードが存在しない場合は、次の手順を実行します。
-
TargetEntity.KeyAttributesに存在し、かつTargetEntity.Attributesにまだ存在しない任意のデータをEntity.Attributesコレクションにコピーします。 -
Createを呼び出します。 -
UpsertResponse.RecordCreated を
trueに設定します。 -
エンティティから
Targetを作成し、id操作のCreate結果を UpsertResponse.Target の値として作成します。 - UpsertResponse を返します。
-
次の図は、 UpsertRequest を受信したときのサーバー上のプロセスを示しています。
リクエスト作成のガイダンス
代替キーを使用してレコードを識別する場合は、保存するデータを表す要求の部分に代替キー データを含めないでください。
Web API を使用していて、SDK for .NET に慣れていない場合は、前に説明したサーバー側のプロセスに従うのが難しい場合があります。 Web API には、前述の説明と図で使用した SDK オブジェクトと同じオブジェクト モデルはありませんが、次の表に示すようにデータをマップできます。
| Web API | SDK | 説明 |
|---|---|---|
| URL のキー値 | Entity.KeyAttributes プロパティ | レコードを識別する代替キー データが含まれています。 |
| 要求の本文 | UpsertRequest.Target プロパティに設定されたエンティティ |
Create または Update に使用するデータが含まれています。 |
サーバーは前述のようにこれらの要求を処理しますが、次のように考えることができます。
- レコードが存在する場合: サーバーは、URL 内の代替キー値に対する要求の本文でデータ セットを削除するため、それを含める意味はありません。 これにより、代替キー値を使用してレコードを識別しているときに、レコードの代替キー値を更新できなくなります。 代替キーの値は、主キーまたは別の代替キーのセットを使用して変更できます。
- レコードが存在しない場合: サーバーは、URL の代替キーで指定された値と データが異なる場合でも 、要求の本文で設定された代替キー値を使用して新しいレコードを作成します。 要求の本文に代替キー データがない場合、サーバーは URL から要求の本文に代替キー データをコピーします。 URL のキー値と本文の対応するキー値が一致しない状況を回避するには、本文に含めないでください。
Web API の使用
Web API を使用すると、指定したUpsert リソースに HTTP Update要求を送信することで、PATCHとEntitySetメッセージを開始できます。 URL 内のキーによってリソースが識別されます。
UpsertとUpdateの違いは、If-Match: *要求ヘッダーを含めるかどうかによって異なります。
If-Match: *要求ヘッダーを含め、URL のキー値と一致するリソースがない場合、要求は404 Not Found状態コードを返します。
If-Match: * 要求ヘッダーは、PATCH 要求が Update 操作であることを保証します。
If-Match: *要求ヘッダーを含めない場合、PATCH要求はUpsertのように扱われます。 URL 内のキーに一致するレコードが見つからない場合、要求によって新しいレコードが作成されます。 ただし、SDK とは異なり、応答ではレコードが作成されたかどうかは示されません。 状態の応答は、どちらの場合も 204 No Content です。
Prefer: return=representation要求ヘッダを含める場合、システムは201 CreatedのCreateステータスと200 OKのUpdateステータスを返します。 このヘッダーを追加すると、追加の Retrieve 操作が追加されるため、パフォーマンスに影響します。 このオプションを使用する場合は、追加する $select クエリ オプションに主キー値のみが含まれていることを確認してください。 詳細については、以下を参照してください:
PATCH要求を使用すると、If-None-Match: *要求ヘッダーを含めて、レコードのみを作成する場合にUpdateをブロックすることもできます。 詳細については、「 アップサート操作の制限」を参照してください。
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 preference を使用して作成する
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"
}
.NET用SDKを利用する
クライアント アプリケーションでは、
UpsertResponse.RecordCreated プロパティは、レコードが作成されたかどうかを示し、UpsertResponse.Target には、作成または更新されたレコードへの参照が含まれています。
SDK for .NET サンプル コード
Upsert サンプルを使用した Insert レコードの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;
}
}
}