🔖

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;
}

個人的にハマったのでメモです。

Microsoft (有志)

Discussion