1. xConnectクライアントAPI (C#)

xConnectでのオプティミスティック コンカレンシー

Version:
日本語翻訳に関する免責事項

このページの翻訳はAIによって自動的に行われました。可能な限り正確な翻訳を心掛けていますが、原文と異なる表現や解釈が含まれる場合があります。正確で公式な情報については、必ず英語の原文をご参照ください。

xConnectは、オプティミスティック コンカレンシー モデルを実装します。つまり、コンタクトとファセットは読み取られるときにロックされず、クライアントは変更を送信するときに同時実行の競合を予測する必要があります。計算されたファセットは、xConnectサービス レイヤーによって自動的に再試行されます。

次の手順では、コンカレンシーの競合中に何が起こるかについて説明します。

  • コンタクトまたはファセットが作成されると、ストレージプロバイダーによってそのレコードの同時実行トークンが生成されます。

  • コンタクトまたはファセットがリクエストされると、同時実行トークンがレスポンスに含まれます(すべてのコンタクトとファセットにはConcurrencyTokenプロパティーがあります)。

  • コンタクトまたはファセットを変更して保存すると、受信レコードはストレージ内のレコードと比較されます。同時実行トークンが異なる場合は、レコードが最初に取得されてから別のソースによってレコードが更新されたことを意味します。この場合、xConnectは例外をスローし、現在ストレージにあるファセットを返します。

  • この時点で、再試行するかどうかを選択できます。

大事な

オプティミスティック同時実行モデルとは、更新が成功することを保証できないことを意味します。潜在的なコンカレンシーの競合に対処する必要があります。

紛争のリスクを減らす

競合のリスクを軽減するために、コンタクトまたはファセットを取得した後、できるだけ早く更新を送信することをお勧めします。たとえば、SitecoreのWebトラッカーでは、セッション中にファセットを更新することはできません。xConnectクライアント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();
                        }
                    }
                }
            }
        }
    }
}
手記

競合を解決する一般的な方法はありません - 競合を引き起こす可能性のある各操作タイプとファセットタイプを考慮する必要があります。

.ファセットと .Result.CurrentVersion (英語)

SetFacetOperationオブジェクトの .Facetプロパティは、送信しようとしたファセット オブジェクトを表します。.Result.CurrentVersionプロパティは、xConnectによって返される最新のファセットを表します。返されたファセットを使用して、エンドユーザーに正しい値を選択するように促したり、エンドユーザーにプロンプトを表示せずに変更を.Result.CurrentVersionにマップして再試行したりできます。

大事な

.Facetを再送信しないでください - 同期トークンが古くなっています。任意のプロパティを.Result.CurrentVersionオブジェクトにマップし、代わりにそれを送信する必要があります。

この記事を改善するための提案がある場合は、 お知らせください!