前回に引き続き Dynamics CRM Online 2015 Update 1 で提供される
Web API 開発者プレビューについて紹介します。連載記事になるため
第 1 回からご覧ください。
Web API 開発者プレビュー その 1
Web API 開発者プレビュー その 2
Web API 開発者プレビュー その 3
Web API 開発者プレビュー その 4
Web API ではオプティミスティック同時実行制御もサポートしています。
オプティミスティック同時実行制御の SDK サポートは以下の記事を
ご覧ください。
Dynamics CRM Online 2015 Update 1 SDK 新機能: オプティミスティック同時実行制御
レコード Version の取得
まず取引先企業を 1 件作成してから、レコードの Version 情報を取得します。
プログラムの実装
1 前回利用した Visual Studio ソリューションを開き、Program.cs
ファイルを開きます。新しく以下のメソッドを追加します。
public async Task RunOptimisticConcurrency(string accessToken)
{
// HttpClient の作成
using (HttpClient httpClient = new HttpClient())
{
// Web API アドレスの作成
string serviceUrl = serverUrl + "/api/data/";
// ヘッダーの設定
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}
}
2. Main メソッドの以下のコードを書き換えて、新しい
メソッドを呼ぶように変更します。
元)
Task.WaitAll(Task.Run(async () => await app.RunUpsert(result.AccessToken)));
変更後)
Task.WaitAll(Task.Run(async () => await app.RunOptimisticConcurrency(result.AccessToken)));
3. 新しく追加した RunOptimisticConcurrency メソッド内に
以下のコードを追加して、取引先企業を 1 件作成します。
// 取引先企業オブジェクトの作成
Account account = new Account();
account.name = "同時実行制御デモ";
account.telephone1 = "555-5555";
// 送信リクエストの作成
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, serviceUrl + "accounts");
request.Content = new StringContent(JsonConvert.SerializeObject(account, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore }));
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
// リクエストの送信
var result =await httpClient.SendAsync(request);
4. 次に作成したレコードを取得して、バージョン番号を
表示します。以下のコードを追加します。
// 作成したレコードの取得
var retrieveRes = await httpClient.GetAsync(result.Headers.Location);
JToken jToken = JObject.Parse(retrieveRes.Content.ReadAsStringAsync().Result);
account = JsonConvert.DeserializeObject<Account>(jToken.ToString());
string version = jToken["@odata.etag"].ToString();
// バージョンの表示
Console.WriteLine("レコードのバージョンは {0} です。", version );
Console.Read();
動作確認
1. F5 キーを押下してプログラムを実行します。
2. 認証ダイアログが表示されたらログインします。
![image image]()
3. レコードのバージョン情報が表示されることを確認します。
![image image]()
バージョンを指定したレコードの取得
一度レコードを取得した後、他のユーザーがそのレコードを更新
していないか確認する場面があると思います。その方法として、
バージョン番号が異なる場合のみレコードを取得できます。
プログラムの実装
1 先ほど取得したバージョン番号を指定したレコード取得を
行います。以下のコードを追加します。ポイントは Header
に If-None-Match とバージョン番号を追加することです。
// レコードのバージョンが異なる場合のみレコードを取得
HttpRequestMessage retrieveReq = new HttpRequestMessage(HttpMethod.Get, result.Headers.Location);
retrieveReq.Headers.Add("If-None-Match", version);
2. リクエストの送信と結果を表示します。
// レコードの取得
try
{
retrieveRes = await httpClient.SendAsync(retrieveReq);
jToken = JObject.Parse(retrieveRes.Content.ReadAsStringAsync().Result);
account = JsonConvert.DeserializeObject<Account>(jToken.ToString());
string newVersion = jToken["@odata.etag"].ToString();
// バージョンの表示
Console.WriteLine("レコードのバージョンは {0} です。", newVersion);
}
catch(Exception ex)
{
Console.WriteLine("レコードはまだ更新されていません");
}
Console.Read();
動作確認
1. F5 キーを押下してプログラムを実行します。
2. 認証ダイアログが表示されたらログインします。
3. レコードのバージョン情報が表示されることを確認します。
![image image]()
4. Enter キーを押下してプログラムを進めます。以下の画面
が出ることを確認します。
![image image]()
5. Enter キーを押下してプログラムを終了した後、再度 F5
キーを押下してプログラムを実行します。
6. 認証ダイアログが表示されたらログインします。
7. レコードのバージョン情報が表示されることを確認します。
8. 再度レコードを取得する前に、ブラウザからレコードを更新します。
ブラウザで Dynamics CRM Online に接続してレコードの一部を変更
します。その後プログラムに戻り、Enter キーを押下します。
レコードのバージョンが変わるため、以下のように表示されます。
![image image]()
バージョンを指定したレコードの更新
最後にレコードの更新を同じようにやってみます。ここまで既に
いくつかレコードを作成しているため、このタイミングで一度
ブラウザで「同時実行制御デモ」と名前がついているものは削除
することをお勧めします。
1. 更新要求を作成します。今回はバージョンが変わっていない場合に
更新を行いたいので、If-Match ヘッダーを設定します。
// レコードの更新
account.name = "同時実行制御デモ 更新";
// バージョンが一致する場合にのみ更新
HttpRequestMessage updateReq = new HttpRequestMessage(new HttpMethod("PATCH"), result.Headers.Location);
updateReq.Content = new StringContent(JsonConvert.SerializeObject(account, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore }));
updateReq.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
updateReq.Headers.Add("If-Match", version);
2. リクエストを送信して、結果を表示します。
try
{
HttpResponseMessage updateRes = await httpClient.SendAsync(updateReq);
if (updateRes.IsSuccessStatusCode)
Console.WriteLine("レコードを更新しました。", version);
else
Console.WriteLine("レコードが更新できませんでした。");
}
catch (Exception ex)
{
}
動作確認
1. F5 キーを押下してプログラムを実行します。
2. 認証ダイアログが表示されたらログインします。
3. レコードのバージョン情報が表示されることを確認します。
![image image]()
4. Enter キーを押下します。ブラウザでレコードを更新していない
ため、以下のメッセージが表示されます。
![image image]()
5. Enter キーを押下します。レコードをが更新されたかブラウザで
確認します。
![image image]()
![image image]()
6. Enter キーを押下してプログラムを終了した後、再度 F5 キーを
押下してプログラムを実行します。
7. 認証ダイアログが表示されたらログインします。
8. レコードのバージョン情報が表示されることを確認します。
![image image]()
9. 今回はブラウザより手動でレコードを変更します。レコードを
変更した後、プログラムに戻り Enter キーを押下します。手動で
レコードを編集したため、意図したとおり更新に失敗しました。
![image image]()
最後に今回追加したメソッドを示します。
public async Task RunOptimisticConcurrency(string accessToken)
{
// HttpClient の作成
using (HttpClient httpClient = new HttpClient())
{
// Web API アドレスの作成
string serviceUrl = serverUrl + "/api/data/";
// ヘッダーの設定
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// 取引先企業オブジェクトの作成
Account account = new Account();
account.name = "同時実行制御デモ";
account.telephone1 = "555-5555";
// 送信リクエストの作成
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, serviceUrl + "accounts");
request.Content = new StringContent(JsonConvert.SerializeObject(account, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore }));
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
// リクエストの送信
var result = await httpClient.SendAsync(request);
// 作成したレコードの取得
var retrieveRes = await httpClient.GetAsync(result.Headers.Location);
JToken jToken = JObject.Parse(retrieveRes.Content.ReadAsStringAsync().Result);
account = JsonConvert.DeserializeObject<Account>(jToken.ToString());
string version = jToken["@odata.etag"].ToString();
// バージョンの表示
Console.WriteLine("レコードのバージョンは {0} です。", version);
Console.Read();
// レコードのバージョンが異なる場合のみレコードを取得
HttpRequestMessage retrieveReq = new HttpRequestMessage(HttpMethod.Get, result.Headers.Location);
retrieveReq.Headers.Add("If-None-Match", version);
// レコードの取得
try
{
retrieveRes = await httpClient.SendAsync(retrieveReq);
jToken = JObject.Parse(retrieveRes.Content.ReadAsStringAsync().Result);
account = JsonConvert.DeserializeObject<Account>(jToken.ToString());
string newVersion = jToken["@odata.etag"].ToString();
// バージョンの表示
Console.WriteLine("レコードのバージョンは {0} です。", newVersion);
}
catch(Exception ex)
{
Console.WriteLine("レコードはまだ更新されていません");
}
Console.Read();
// レコードの更新
account.name = "同時実行制御デモ 更新";
// バージョンが一致する場合にのみ更新
HttpRequestMessage updateReq = new HttpRequestMessage(new HttpMethod("PATCH"), result.Headers.Location);
updateReq.Content = new StringContent(JsonConvert.SerializeObject(account, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore }));
updateReq.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
updateReq.Headers.Add("If-Match", version);
try
{
HttpResponseMessage updateRes = await httpClient.SendAsync(updateReq);
if (updateRes.IsSuccessStatusCode)
Console.WriteLine("レコードを更新しました。", version);
else
Console.WriteLine("レコードが更新できませんでした。");
}
catch (Exception ex)
{
}
}
}
まとめ
楽観的同時実行制御は、今回紹介した読み取りと更新以外に、
削除処理も制御できますので、是非お試しください!
- 中村 憲一郎