---
title: "xConnectにおける楽観的並行性"
slug: "optimistic-concurrency-in-xconnect"
abstract: "xConnectにおける楽観的並行モデルの概要。これは、連絡先やファセットが読み取ってもロックされず、クライアントが変更を提出する際に並行性の競合を予期しなければならないことを意味します。"
---
# xConnectにおける楽観的並行性

xConnectは楽観的並行性モデルを実装しています。これは、連絡先やファセットが読み取ってもロックされず、クライアントが変更を提出する際に並行性の競合を予期しなければならないことを意味します。計算されたファセットはxConnectサービスレイヤーによって自動的に再試されます。

以下の手順で、並行性競合時に何が起こるかを説明します。

-   コンタクトやファセットが作成されると、ストレージプロバイダーによってそのレコードに対して並行トークンが生成されます。
-   コンタクトやファセットがリクエストされると、コンカレンシートークンは応答に含まれます(すべてのコンタクトとファセットは ConcurrencyToken プロパティを持ちます)。
-   コンタクトやファセットが修正されて保存されると、受信レコードがストレージ内のレコードと比較されます。同時実行トークンが異なる場合、そのレコードは最初に取得された後に別のソースによって更新されていることを意味します。この場合、xConnectは例外を投げて、現在ストレージにあるファセットを返します。
-   この時点で、再挑戦するかどうかを選べます。

> !重要楽観的並行性モデルとは、アップデートが必ず成功することを保証できないことを意味します。潜在的な並行性競合に対処しなければなりません。

## 紛争のリスク低減

競合のリスクを減らすために、連絡先やファセットを取得した後はできるだけ早く更新を送信することをお勧めします。例えば、Sitecoreのウェブトラッカーではセッション中にファセットの更新はできません。xConnect Client APIを使って即時にxConnectに更新を送信する必要があります。競合リスクを評価する際には以下の要素を考慮してください:

-   特定の側面や連絡先を更新できるシステムはどれだけあるか
-   特定のファセットや識別子がどのくらいの頻度で更新される可能性が高いか

## 競合する作戦の再挑戦

xConnectは、何らかの理由で1つ以上の操作が失敗した場合、例外をスローします。競合により操作が失敗した場合、以下のことができます:

-   エンドユーザーに促さずに失敗した操作を自動的に再試行できます
-   エンドユーザーがxConnectに提出する前に競合解決の動作を選択できるようにし、その後(例えば一括インポート時など)失敗した操作を自動的に再試行できるようにします。
-   エンドユーザーに各競合を手動で解決するよう促す
-   何もしない

> !注バッチ内で成功した操作は、1つ以上の操作が失敗してもロールバックされません。

以下の例は、競合によって失敗したすべての操作を取得し、再試行する方法を示しています:

using Sitecore.XConnect.Collection.Model;
using Sitecore.XConnect;
using System;
using System.Threading.Tasks;
using System.Linq;
using Sitecore.XConnect.Operations;
using Sitecore.XConnect.Client;

namespace Documentation
{
 public class RetryOperation
 {
 public async void ExampleAsync()
 {
 using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
 {
 try
 {
 // Get contact
 var reference = new Sitecore.XConnect.ContactReference(Guid.Parse("B9814105-1F45-E611-82E6-34E6D7117DCB"));

 Task<Sitecore.XConnect.Contact> contactTask = client.GetAsync<Sitecore.XConnect.Contact>(reference, new Sitecore.XConnect.ContactExpandOptions(PhoneNumberList.DefaultFacetKey)
 {
 });

 Sitecore.XConnect.Contact contact = await contactTask;

 // Update some properties on an existing facet
 PhoneNumberList phoneNumberList = contact.PhoneNumbers();

 phoneNumberList.PreferredKey = "Work";
 phoneNumberList.PreferredPhoneNumber = new PhoneNumber("44", "5555555");

 // Set updated facet
 client.SetPhoneNumbers(contact, phoneNumberList);

 // Submit changes
 await client.SubmitAsync();
 }
 catch (XdbExecutionException ex)
 {
 // Phone numbers changed since the facet was retrieved - a conflict error is thrown
 // Get all operations where a conflict occurred - you must do this per operation type
 var setPhoneOperations = ex.GetOperations(client).OfType<SetFacetOperation<PhoneNumberList>>()
 .Where(x => x.Result.Status == SaveResultStatus.Conflict);

 // Cycle through each operation that set a PhoneNumberList facet
 foreach (var q in setPhoneOperations)
 {
 PhoneNumberList yourFacetValue = q.Facet; // Facet values that you tried to save - sync token is OUT OF DATE
 PhoneNumberList currentFacetValue = q.Result.CurrentVersion; // Current value returned by xConnect - sync token is CORRECT

 // At this point you can either resolve conflicts automatically OR present the end user
 // with a message. In this scenario, we have decided to overwrite the facet values that were returend
 // by xConnect with our own values. It is entirely up to you what the logic should be.

 currentFacetValue.PreferredKey = yourFacetValue.PreferredKey;
 currentFacetValue.PreferredPhoneNumber = yourFacetValue.PreferredPhoneNumber;

 // Set the facet values again - note that if we passed in yourFacetValue, which has an out of date sync token
 // the operation would fail. If the facet has changed AGAIN since it was retrieved, the operation will fail and you must
 // choose whether to retry again.

 client.SetFacet<PhoneNumberList>(q.FacetReference, currentFacetValue);

 await client.SubmitAsync();
 }
 }
 }
 }

 public void ExampleSync()
 {
 using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
 {
 var preferredKey = "Work";
 var preferredPhoneNumber = new PhoneNumber("44", "5555555");

 try
 {
 // Get contact
 var reference = new Sitecore.XConnect.ContactReference(Guid.Parse("B9814105-1F45-E611-82E6-34E6D7117DCB"));

 Sitecore.XConnect.Contact contact = client.Get<Sitecore.XConnect.Contact>(
 reference,
 new ContactExpandOptions(PhoneNumberList.DefaultFacetKey)
 {
 });

 // Update some properties on an existing facet
 PhoneNumberList phoneNumberList = contact.PhoneNumbers();

 phoneNumberList.PreferredKey = preferredKey;
 phoneNumberList.PreferredPhoneNumber = preferredPhoneNumber;

 // Set updated facet
 client.SetPhoneNumbers(contact, phoneNumberList);

 // Submit changes
 client.Submit();
 }
 catch (AggregateException ex)
 {
 // Phone numbers changed since the facet was retrieved - a conflict error is thrown
 // Get all operations where a conflict occurred - you must do this per operation type
 foreach (var e in ex.Flatten().InnerExceptions)
 {
 if (e is FacetOperationException exception && exception.Result == SaveResultStatus.Conflict)
 {
 // At this point you can either resolve conflicts automatically OR present the end user
 // with a message. In this scenario, we have decided to overwrite the facet values that were returned
 // by xConnect with our own values. It is entirely up to you what the logic should be.

 // Set the facet values again.
 // If the facet has changed AGAIN since it was retrieved, the operation will fail and you must
 // choose whether to retry again.
 var entityId = exception.EntityId;
 var facetKey = exception.FacetKey;

 Sitecore.XConnect.Contact contact = client.Get<Sitecore.XConnect.Contact>(
 new ContactReference((Guid)entityId),
 new ContactExpandOptions(facetKey));

 PhoneNumberList phoneNumberList = contact.PhoneNumbers();

 phoneNumberList.PreferredKey = preferredKey;
 phoneNumberList.PreferredPhoneNumber = preferredPhoneNumber;

 client.SetFacet<PhoneNumberList>(contact, phoneNumberList);

 client.Submit();
 }
 }
 }
 }
 }
 }
}

> !注競合を解決する一般的な方法はなく、競合を引き起こす可能性のある各操作タイプやファセットタイプを考慮しなければなりません。

### .ファセット対 。結果。現在のバージョン

各SetFacetOperationオブジェクトの.Facetプロパティは、提出しようとしたファセットオブジェクトを表しています。.Result.CurrentVersionプロパティはxConnectから返された最新のファセットを表しています。返されたファセットを使って、エンドユーザーに正しい値を選ぶよう促したり、変更を.Result.CurrentVersionにマッピングして再挑戦したりできます。

> !重要再送信はしないでください.Facet同期トークンは有効です。プロパティを.Result.CurrentVersionオブジェクトにマッピングして、そちらを提出してください。