Dynamics CRM Online 2015 Update 1 SDK 新機能: オプティミスティック同時実行制御

みなさん、こんにちは。

今回は Dynamics CRM Online 2015 Update 1 で提供されるプラット
フォームと SDK の新機能よりオプティミスティック同時実行制御を
紹介します。

概要

Dynamics CRM では、複数ユーザーが同じレコードを同時に編集した
場合、最後に更新した内容が反映されます。この挙動が望ましくない
環境では、今回リリースされたオプティミスティック同時実行制御を
利用できます。

具体的にはレコードを読み取ったタイミングでレコードのバージョン
番号を取得しておき、変更時にバージョン番号が同じか確認できます。
もしバージョン番号が異なる場合は、他のユーザーまたはシステムが
レコードを更新したことになります。

動作確認

今回は取引先企業レコードの更新における制御を試してみます。

プログラムの作成

1. Visual Studio を起動します。新しいプロジェクトをクリック
して、Visual C# | Windows デスクトップ | コンソールアプリ
ケーションを選択します。.NET Framework 4.5.2 を指定します。
任意の名前を付けて、「OK」をクリックします。

image

2. ソリューションエクスプローラーより作成したプロジェクトを
右クリックして、NuGet パッケージの管理をクリックします。

image

3. 左ペインで「オンライン」を選択後、右上の検索ボックスにて
crmsdk を検索します。一覧より Microsoft Dynamics CRM 2015
SDK client and portal assemblies を選択して、「インストール」を
クリックします。

image

4. インストールが完了したら「閉じる」で NuGet パッケージの
管理を閉じます。

5. Main メソッドに以下のコードを追加します。これで Dynamics
CRM 組織への接続が作成できます。接続文字は適宜変更します。

CrmConnection conn = CrmConnection.Parse("Url=https://contoso.crm.dynamics.com; Username=someone@contoso.onmicrosoft.com; Password=password;");
OrganizationService service = new OrganizationService(conn);

6. 続いて以下のコードを追加します。取引先企業レコードが 1 件
追加されます。このレコードで同時実行制御を行います。

Entity account = new Entity("account");
account["name"] = "同時制御テスト";

Guid accountId = service.Create(account);

7. 作成したレコードを取得し直しします。バージョン番号が
取得できます。

account = service.Retrieve("account", accountId, new ColumnSet("name"));
Console.WriteLine("バージョン番号: {0}", account.RowVersion);
Console.Read();

8. 以下のコードで更新要求を作成します。ここでバージョン
番号が一致した場合のみ更新するように指定します。

account["name"] = "同時制御テスト更新";

UpdateRequest request = new UpdateRequest()
{
    Target = account,
    // バージョンが一致する場合のみ更新
    ConcurrencyBehavior = ConcurrencyBehavior.IfRowVersionMatches
};

9. バージョン番号が異なる場合処理に失敗するため、以下の
ように try/catch を実装します。

try
{
    service.Execute(request);
    Console.WriteLine("更新に成功しました");
}
catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> e)
{
    // バージョンが一致しない場合 (ConcurrencyVersionMismatch)
    if (e.Detail.ErrorCode == -2147088254)
    {
        var currentaccount = service.Retrieve("account", accountId, new ColumnSet());
        Console.WriteLine("更新に失敗しました。バージョン番号: {0}", currentaccount.RowVersion);
    }
    else
        throw e;
}

Console.Read();

プログラムの実行

1. F5 キーを押下してプログラムを実行します。

2. 作成されたレコードのバージョン番号が表示されます。

image

3. Enter キーを押下して変更を実施します。エラーはなく処理が
完了することを確認します。

4. ブラウザより作成されたレコードを削除した後、プログラムを
再度実行します。新しいレコードが作成され、バージョン番号が
表示されます。

image

5. ブラウザより、作成されたレコードを手動で編集します。

image

6. プログラムに戻って Enter キーを押下します。バージョン番号
が変わっているため、処理に失敗します。

image

失敗のパターン

上記を含めて、3 つの失敗パターンが存在します。

ConcurrencyVersionMismatch (code=-2147088254)
オプティミスティック同時実行制御を指定したがバージョン
番号が一致しない場合

ConcurrencyVersionNotProvided (code= -2147088253
オプティミスティック同時実行制御を指定したがバージョン
番号を提供しなかった場合

OptimisticConcurrencyNotEnabled (code=-2147088243)
オプティミスティック同時実行制御を指定したが、サーバー
側で対応していない場合

オプティミスティック同時制御がサポートされる処理

オプティミスティック同時制御は更新と削除要求でサポート
されます。また挙動は以下の 2 つを指定できます。

ConcurrencyBehavior.IfRowVersionMatches
ConcurrencyBehavior.AlwaysOverwrite

制限

プラットフォームレベルで対応している機能のため、SDK
クライアントやプラグインからも同じ挙動が保障されますが、
ブラウザからレコードを保存する場合はこれまで通りの
動きとなります。

まとめ

これまで同時実行制御が必要な環境では、最終更新日を
明示的に確認する必要がありましたが、今回のリリースで
より簡単に実装できます。是非お試しください!

- 中村 憲一郎