🔖
Durable Functions で派生クラスのシリアライズを設定したオブジェクトを Activity のパラメーターに渡す方法
はじめに
Durable Functions (.NET Isolated) で、モデルに JsonDerivedType
を付けているときにハマった話です。例えば「ProgressBase
(親)と QueryGenerationProgress
(子)みたいな型があって、オーケストレーターからは子インスタンスを渡し、Activity 側の引数は ProgressBase
で受けて型判定をして処理を振り分けたい」という、よくあるパターンです。これをそのまま素直に実装すると、受信側で $type
(型ディスクリミネータ)が見当たらず、デシリアライズに失敗することがあります。
ざっくり言うと、Durable Functions は内部で System.Text.Json による再シリアライズを挟みます。このときシリアライザーに設定される型が基底型 (今回の場合は ProgressBase
) として扱われていない(例: object
や派生型のまま)と、$type
が書き出されないケースがあり、結果として Activity 側で ProgressBase
にデシリアライズできずにエラーになってしまいます。
解決策
パラメーターとして渡したい型をラップする DTO を 1 枚噛ませて、それを渡すようにするのが一番簡単です。
例えば以下のようなクラスを定義します。
public record ProgressEnvelope(ProgressBase Progress);
この型を引数として使用することで ProgressBase
という型情報を失わないようにします。
あとは、このクラスを使って普通にオーケストレーター関数から Activity を呼び出します。
await context.CallActivityAsync(
nameof(ReportProgressActivity),
new ProgressEnvelope(progress));
Activity 関数も引数で ProgressEnvelope
を受け取るようにします。
[Function(nameof(ReportProgressActivity))]
public Task ReportProgressAsync([ActivityTrigger] ProgressEnvelope envelope)
{
ProgressBase progress = envelope.Progress; // ちゃんとデシリアライズされてる
// 以降はお好きに処理してください
return Task.CompletedTask;
}
個人的にハマったのでメモです。
Discussion