Power Automate × Dataverseでトランザクション|変更セット要求の使い方と親子作成のコツ

Power Automateを使ってDataverseのデータを更新するとき、「もしフローの途中でエラーが起きたら、書き込み済みのデータをどうするか」という問題は常に付きまとう。

Power Automateには『変更セット要求を実行する(Changeset)』アクションがあるから大丈夫、と思ってたけど、意外とできることが少なかった。

そこで今回は、Power Automateの「変更セット要求を実行する」アクションを詳しく調査してみたので、その結果の紹介と、このアクションを使ってもよい場面とプラグイン(Pro Code)を使うべき場面を考察してみる。

スポンサーリンク

トランザクションについて

改めて「トランザクション」とは「一連の処理を『全部やる』か『全部やらない』か、どっちかにする仕組み」のこと。

わかりやすい例が「銀行振込」で、
・Aさんの口座から1万円引く
・Bさんの口座に1万円足す

もし「1」だけ成功して、システムエラーで「2」が失敗したら、Aさんの1万円は消えてしまう。

だから、途中で何かが失敗したら、最初から何もなかったことにして(ロールバックして)、Aさんの口座残高を元に戻す。 これが「トランザクション処理」。

トランザクションの実現「変更セット要求を実行する」アクション

Power AutomateにもDataverseでのトランザクションを実現するための機能は用意されていて、それが「変更セット要求を実行する」アクション。
「スコープ(Scope)」のように、枠の中にアクションを入れることができて、これらの処理は、「すべて成功するか、すべて失敗するか」を保証してくれる。

もし3つ目の処理でエラーが起きたら、1つ目と2つ目の書き込みも「なかったこと」にしてくれるので(ロールバックされる)、中途半端なデータ残りとかを防げる。

動作確認

まずは簡単な動作確認。

今回は動作確認のため、注文テーブルと注文明細テーブルを用意。

まずはフローに「変更セット要求」を追加して、
Power Automate 変更セット要求を実行する アクション
注文明細を作成するアクションを3つ入れる。
Power Automate 変更セット要求を実行する アクションに他のアクションを追加
この実行が成功すると、注文明細レコードが3つ作成される。

ここで3つ目の作成で個数にマイナスを設定し、実行すると、
※注文明細の個数は1以上となるようDataverse側で設定済み
フローは(3つ目のアクションは)もちろん失敗。
3つ目はもちろん、1つ目と2つ目の注文明細も作られず、中途半端なゴミデータが残らない。
ちなみに、「作成」だけでなく「削除」もロールバックしてくれる。


ということで、失敗時にしっかりロールバックされるので、トランザクションが効いていることが確認できる。

「変更セット要求の実行」内でリレーション(親子関係)をつくる

「変更セット要求の実行」内で、リレーション(親子関係)を持ったレコードの作成を行う場合は、ひと工夫が必要。

これは、「変更セット要求」はスコープ内のアクションを一気に送信するため、変更セット要求内で作成する親レコードの情報(GUID)を、子レコードの作成時に渡すことができないため。
まずはguid関数を使ってIDを作成し、それを親レコードの作成時に指定する。
Power Automate 変更セット要求を実行する リレーションの構築
そしたら子レコードを作る際、親レコードを指定する欄で、その親レコードのGUIDを使ってODataIDを入力する。
そうすると、親子関係のレコードを「変更セット要求」内で作成することができる。
ちなみに、「変更セット要求」内の実行順序はOData側の仕様で保証されているらしい。
そのため、「親→子」の順にスコープに入れれば、親が作られた後に、その親レコードのGUIDを使用して子レコードをリレーション込みで作成することができる。

ODataの仕様 (OData v4.0 Part 1, 11.7.4):
“Requests within a change set MUST be executed sequentially.” (変更セット内のリクエストは、順次実行されなければならない。)

もちろん「子→親」の順にスコープに入れると、トランザクションは失敗する。

トラブルシューティング:変更セット要求でInternalServerErrorが起きる場合は「アクション名」に注意

変更セット要求の中のアクションの名前が「日本語」で作られている場合、各アクションでInternalServerErrorが発生し、フローは失敗する。

※変更セット要求はBad Gatewayエラーになる。
※ただし、なぜかレコードは作成される。

そこで、変更セット要求内のアクションは、英名に変えること。
こちらのサイトを参考にさせていただきました。

Power Automate「変更セット要求」の難しい点と使い所

最後、実際に動作検証を行った結果見えてきた、「変更セット要求」アクションの難しい点を2つと、使っても大丈夫な場面を考察してみる。

1. 実行タイミングの壁

1つ目は「フロー内で実行する必要がある(アクションだから当たり前だけど)」ための実行タイミングについて。

キャンバスアプリなら「ボタン押下時」にフローを呼べばいいけど、モデル駆動型アプリから呼ぶ場合は、どうしても「対象データの保存(トリガー)」が起点になってしまう。
※リボンメニューからフローを呼ぶことも一応できるけど…

そのため、もしフロー側の処理(トランザクション)が失敗した場合、トリガーとして既に保存されてしまったデータを自前で削除(ロールバック)しなければならない。

この「後始末」の実装が意外とめんどくさい。

2.ロジックの壁(条件分岐・ループ不可)

アクションの仕様上、「変更セット要求」スコープの中にはループ(Apply to each)も条件分岐(If)も配置できない。

つまり、「書き込んだ結果を見て、次の処理を変える」といった動的なトランザクションは組めない。

それでも「変更セット要求」が使える場面

以下の条件をすべて満たすなら、手軽な変更セット要求がおすすめ。

  • 厳密な「排他制御」までは不要
  • トランザクション内で複雑な処理(If/Loop)をしない
  • Dataverse内だけで完結する処理である

典型的な成功パターン:
「注文」ヘッダーと「注文明細」を同時に作成する、といった親子関係の登録処理。

ただし、ここに「在庫の引き当て」や「条件による保存ブロック」が入ってくるなら、プラグインの検討が必要になる。

プラグイン(C# / Power Fx)を選ぶべき理由

変更セットの要求ではなく、プラグインを実装するメリットは以下の4つがある。

「保存前(Pre-Operation)」で止められる

モデル駆動アプリで「データを保存する」操作を行う場合も、Pre-Operationで直前に検証し、実際にデータを保存する前に処理をキャンセルできる。

よって、「ゴミデータの削除処理(補償トランザクション)」が不要になる。

複雑なロジックを含めたトランザクション

プラグイン(C#またはローコード)の中であればIfもループも使用できるので、書き込みながら判断する、みたいな処理の実現も可能。

Etag(RowVersion)を使用した厳密な排他制御

標準フローでは実装困難なEtag(RowVersion)を使用した排他制御も、プラグインなら実装可能。

「誰かが編集中のデータを上書きしてしまう」事故を防ぐなら、この方法が最も良いかと。

※ただし、「モデル駆動型アプリ」の「標準の保存機能」と、この「排他」を共存させるのはかなり大変らしい。これについては別記事で書いてみる。

即時のエラーフィードバック(ユーザー画面にアラートを出せる)

フローの場合、失敗通知は「メール」などになりがちだけど、プラグインなら画面上に赤帯のアラート(ビジネスプロセスエラー)を即座に出せる。

ユーザー体験(UX)の差はかなり大きくなる。

まとめ

ということで、Power Automateの「変更セット要求」アクションは気軽に使えるけど、使いどころを選ぶ機能だった。

そのうち、プラグインについても詳しく調査してみて、本当にこれらが実現可能かを見ていきたいなと。

Tips:「変更セット要求」アクションの内部的な仕組み:ODataのBatchリクエスト

「変更セット要求」アクションの裏側はWeb APIの標準仕様である「OData Batch」を使用しているらしい。

普通にアクションを並べると、Power Automateは Dataverse に対して「1行作って」→(完了待ち)→「次も1行作って」という感じで、毎回通信する。

でも、変更セットを使うと、枠の中にあるリクエストを一度にまとめて送りつけるので、受け取ったDataverse側は、全部メモリ上で処理して、問題なければ一気にコミットする。

だから「全部OK」か「全部NG」かが保証される。

コメント

タイトルとURLをコピーしました