Durable Functions アプリをインプロセスから分離ワーカー モデルに移行する (.NET)

このガイドでは、.NET Durable Functions アプリをインプロセス モデルから分離ワーカー モデルに移行する手順について説明します。 インプロセス モデルは、 2026 年 11 月 10 日にサポートが終了します。 その日を過ぎると、セキュリティ更新プログラムやバグ修正は提供されません。 分離ワーカー モデルでは、完全なプロセス制御、標準の.NET依存関係の挿入、最新のプラットフォーム機能へのアクセスも提供されます。

Warnung

インプロセス モデルのサポートは 、2026 年 11 月 10 日に終了します。 今すぐ移行することをお勧めします。 分離ワーカー モデルの背景については、「.NET分離ワーカー プロセスの概要を参照してください。

移行チェックリスト

次のチェックリストを使用して、各移行手順の進行状況を追跡します。

Step セクション
1. 前提条件を確認する 前提条件
2. プロジェクト ファイルを更新する プロジェクト ファイルを更新する
3. Program.csを追加する Program.csの追加
4. パッケージ参照を更新する パッケージ参照を更新する
5. 関数コードを更新する 関数コードを更新する
6. local.settings.json を更新する local.settings.jsonの更新
7. ローカルでテストする ローカルでテストする
8. Azureにデプロイする Azure に配置する

前提条件

  • Azure Functions Core Tools v4.x 以降
  • .NET 8.0 SDK (またはターゲット .NET バージョン)
  • Visual Studio 2022 または VS Code with Azure Functions extension

移行するアプリを特定する (省略可能)

まだインプロセス モデルを使用しているアプリがわからない場合は、次のAzure PowerShellスクリプトを実行します。

$FunctionApps = Get-AzFunctionApp

$AppInfo = @{}

foreach ($App in $FunctionApps)
{
     if ($App.Runtime -eq 'dotnet')
     {
          $AppInfo.Add($App.Name, $App.Runtime)
     }
}

$AppInfo

ランタイムとして dotnet を表示するアプリは、インプロセス モデルを使用します。 dotnet-isolatedを表示するアプリでは、分離された worker モデルが既に使用されています。

プロジェクト ファイルを更新する

進行中(途中段階)

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.1" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.DurableTask" Version="2.13.0" />
  </ItemGroup>
</Project>

その後 (分離ワーカー)

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    <OutputType>Exe</OutputType>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.21.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.2" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.2.1" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.14.1" />
    <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.2.0" />
  </ItemGroup>
  <ItemGroup>
    <Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext"/>
  </ItemGroup>
</Project>

主な変更は、実行可能な出力の種類に切り替え、すべてのMicrosoft.Azure.WebJobs.* パッケージを、Microsoft.Azure.Functions.Worker.* 同等のパッケージに置き換えることです。

Program.csの追加

分離ワーカー モデルには、 Program.cs エントリ ポイントが必要です。 プロジェクト ルートにこのファイルを作成します。 FunctionsStartupStartup.cs クラスがある場合は、それらのサービス登録を ConfigureServices ブロックに移動し、Startup.csを削除します。

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services => {
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
        
        // Add your custom services here (previously in FunctionsStartup)
        // services.AddSingleton<IMyService, MyService>();
    })
    .Build();

host.Run();

パッケージ参照を更新する

Durable Functions パッケージ のマッピング

インプロセス パッケージ 分離ワーカー パッケージ
Microsoft.Azure.WebJobs.Extensions.DurableTask Microsoft.Azure.Functions.Worker.Extensions.DurableTask
Microsoft.DurableTask.SqlServer.AzureFunctions Microsoft.Azure.Functions.Worker.Extensions.DurableTask.SqlServer
Microsoft.Azure.DurableTask.Netherite.AzureFunctions Microsoft.Azure.Functions.Worker.Extensions.DurableTask.Netherite

一般的な拡張機能パッケージのマッピング

進行中 アイソレートワーカー
Microsoft.Azure.WebJobs.Extensions.Storage Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs.Queues.Tables
Microsoft.Azure.WebJobs.Extensions.CosmosDB Microsoft.Azure.Functions.Worker.Extensions.CosmosDB
Microsoft.Azure.WebJobs.Extensions.ServiceBus Microsoft.Azure.Functions.Worker.Extensions.ServiceBus
Microsoft.Azure.WebJobs.Extensions.EventHubs Microsoft.Azure.Functions.Worker.Extensions.EventHubs
Microsoft.Azure.WebJobs.Extensions.EventGrid Microsoft.Azure.Functions.Worker.Extensions.EventGrid

Important

Microsoft.Azure.WebJobs.* 名前空間と Microsoft.Azure.Functions.Extensions への参照をプロジェクトから削除します。

関数コードを更新する

このセクションでは、各Durable Functionsの種類のコード変更について説明します。 アプリで使用する関数の種類のセクションに移動します。

API ごとの完全なマッピングについては、 API リファレンスを参照してください

名前空間の変更

// Before (In-Process)
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Extensions.Http;

// After (Isolated Worker)
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;
using Microsoft.DurableTask.Entities;

関数属性の変更

// Before (In-Process)
[FunctionName("MyOrchestrator")]

// After (Isolated Worker)
[Function(nameof(MyOrchestrator))]

オーケストレーター関数の変更

以前(進行中):

[FunctionName("OrderOrchestrator")]
public static async Task<OrderResult> RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context,
    ILogger log)
{
    var order = context.GetInput<Order>();
    
    await context.CallActivityAsync("ValidateOrder", order);
    await context.CallActivityAsync("ProcessPayment", order.Payment);
    await context.CallActivityAsync("ShipOrder", order);
    
    return new OrderResult { Success = true };
}

アフター(アイソレーテッドワーカー):

[Function(nameof(OrderOrchestrator))]
public static async Task<OrderResult> OrderOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    ILogger logger = context.CreateReplaySafeLogger(nameof(OrderOrchestrator));
    var order = context.GetInput<Order>();
    
    await context.CallActivityAsync("ValidateOrder", order);
    await context.CallActivityAsync("ProcessPayment", order.Payment);
    await context.CallActivityAsync("ShipOrder", order);
    
    return new OrderResult { Success = true };
}

主な違い

特徴 処理中 孤立作業者
コンテキストの種類 IDurableOrchestrationContext TaskOrchestrationContext
Logger ILogger パラメーター context.CreateReplaySafeLogger()
特性 [FunctionName] [Function]

アクティビティ関数の変更

以前(進行中):

[FunctionName("ValidateOrder")]
public static bool ValidateOrder(
    [ActivityTrigger] Order order,
    ILogger log)
{
    log.LogInformation("Validating order {OrderId}", order.Id);
    return order.Items.Any() && order.TotalAmount > 0;
}

アフター(アイソレーテッドワーカー):

[Function(nameof(ValidateOrder))]
public static bool ValidateOrder(
    [ActivityTrigger] Order order,
    FunctionContext executionContext)
{
    ILogger logger = executionContext.GetLogger(nameof(ValidateOrder));
    logger.LogInformation("Validating order {OrderId}", order.Id);
    return order.Items.Any() && order.TotalAmount > 0;
}

クライアント関数の変更

以前(進行中):

[FunctionName("StartOrder")]
public static async Task<IActionResult> StartOrder(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
    [DurableClient] IDurableOrchestrationClient client,
    ILogger log)
{
    var order = await req.ReadFromJsonAsync<Order>();
    string instanceId = await client.StartNewAsync("OrderOrchestrator", order);
    
    return client.CreateCheckStatusResponse(req, instanceId);
}

アフター(アイソレーテッドワーカー):

[Function("StartOrder")]
public static async Task<HttpResponseData> StartOrder(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
    [DurableClient] DurableTaskClient client,
    FunctionContext executionContext)
{
    ILogger logger = executionContext.GetLogger("StartOrder");
    var order = await req.ReadFromJsonAsync<Order>();
    string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
        nameof(OrderOrchestrator), 
        order
    );
    
    return await client.CreateCheckStatusResponseAsync(req, instanceId);
}

クライアントの種類の変更

進行中 アイソレートワーカー
IDurableOrchestrationClient DurableTaskClient
StartNewAsync() ScheduleNewOrchestrationInstanceAsync()
CreateCheckStatusResponse() CreateCheckStatusResponseAsync()
HttpRequest / IActionResult HttpRequestData / HttpResponseData

ポリシーの変更を再試行する

インプロセスはRetryOptionsCallActivityWithRetryAsyncを使用します。 分離ワーカーは、TaskOptionsと標準のCallActivityAsyncを使用します。

以前(進行中):

var retryOptions = new RetryOptions(
    firstRetryInterval: TimeSpan.FromSeconds(5),
    maxNumberOfAttempts: 3);

string result = await context.CallActivityWithRetryAsync<string>(
    "MyActivity", retryOptions, input);

アフター(アイソレーテッドワーカー):

var retryOptions = new TaskOptions(
    new TaskRetryOptions(new RetryPolicy(
        maxNumberOfAttempts: 3,
        firstRetryInterval: TimeSpan.FromSeconds(5))));

string result = await context.CallActivityAsync<string>(
    "MyActivity", input, retryOptions);

エンティティ関数の変更

以前(進行中):

[FunctionName(nameof(Counter))]
public static void Counter([EntityTrigger] IDurableEntityContext ctx)
{
    switch (ctx.OperationName.ToLowerInvariant())
    {
        case "add":
            ctx.SetState(ctx.GetState<int>() + ctx.GetInput<int>());
            break;
        case "get":
            ctx.Return(ctx.GetState<int>());
            break;
    }
}

アフター(アイソレーテッドワーカー):

[Function(nameof(Counter))]
public static Task Counter([EntityTrigger] TaskEntityDispatcher dispatcher)
{
    return dispatcher.DispatchAsync<CounterEntity>();
}

public class CounterEntity
{
    public int Value { get; set; }
    
    public void Add(int amount) => Value += amount;
    public int Get() => Value;
}

破壊的動作の変更

移行したアプリをテストする前に、これらの変更を確認してください。 API ごとの完全なマッピングについては、 API リファレンスを参照してください

Warnung

シリアル化の既定の変更: 分離されたワーカーは、System.Text.Jsonの代わりに既定でNewtonsoft.Jsonを使用します。 オーケストレーションで複雑なオブジェクトを渡す場合は、シリアル化を慎重にテストしてください。 構成オプションについては、 JSON シリアル化の違い を参照してください。

Warnung

ContinueAsNew の既定の変更: preserveUnprocessedEvents パラメーターの既定値が false (2.x) から true (分離) に変更されました。 オーケストレーションで ContinueAsNew を使用し、破棄される未処理のイベントに依存している場合は、 preserveUnprocessedEvents: falseを明示的に渡します。

RestartAsync の既定の変更: restartWithNewInstanceId パラメーターの既定値が true (2.x) から false (分離) に変更されました。 コードが RestartAsync を呼び出し、生成される新しいインスタンス ID に依存している場合は、 restartWithNewInstanceId: trueを明示的に渡します。

その他の重要な変更点:

  • エンティティ プロキシが削除されましたCreateEntityProxy<T> は使用できません。 Entities.CallEntityAsyncまたはEntities.SignalEntityAsyncを直接使用します。
  • クロスタスクハブ操作が削除されましたtaskHubName/connectionName を受け入れるオーバーロードは使用できません。 同じタスク ハブ操作のみがサポートされます。
  • オーケストレーション履歴が移動されましたDurableOrchestrationStatus.History が状態オブジェクトに表示されなくなります。 DurableTaskClient.GetOrchestrationHistoryAsync を使用してください。

local.settings.jsonの更新

主な変更は、FUNCTIONS_WORKER_RUNTIMEからdotnetdotnet-isolatedを設定することです。

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
    }
}

ストレージ バックエンド構成 (Azure Storage、MSSQL、Netherite、または Durable Task Scheduler) は、移行によって変更されません。 既存のストレージ関連の設定を保持します。

ローカルでテストする

関数アプリをローカルで実行し、すべてのオーケストレーション、アクティビティ、エンティティが正しく動作するかどうかを確認します。

func start

機能を検証する

必要に応じて、次のシナリオをテストします。

  1. HTTP トリガーを使用してオーケストレーションを開始する
  2. オーケストレーションの状態を監視する
  3. アクティビティの実行順序を確認する
  4. エンティティ操作 (該当する場合) をテストする
  5. Application Insights テレメトリを確認する

Azure にデプロイ

デプロイ スロットを使用してダウンタイムを最小限に抑えます。

  1. 関数アプリのステージング スロットを作成します。
  2. ステージング スロットの構成を更新します。
    • FUNCTIONS_WORKER_RUNTIMEdotnet-isolated に設定します。
    • 必要に応じて.NETスタック バージョンを更新します。
  3. 移行されたコード をステージング スロットにデプロイします。
  4. ステージング スロットで十分にテストします。
  5. スロット スワップを実行 し、変更を本番環境に反映します。

アプリケーション設定を更新する

Azure ポータルまたは CLI を使用して、次の手順を実行します。

az functionapp config appsettings set \
    --name <FUNCTION_APP_NAME> \
    --resource-group <RESOURCE_GROUP> \
    --settings FUNCTIONS_WORKER_RUNTIME=dotnet-isolated

スタックの構成を更新

別の.NET バージョンを対象とする場合:

az functionapp config set \
    --name <FUNCTION_APP_NAME> \
    --resource-group <RESOURCE_GROUP> \
    --net-framework-version v8.0

移行に関する一般的な問題

問題: アセンブリの読み込みエラー

現象:Could not load file or assembly エラー。

ソリューション: すべてのMicrosoft.Azure.WebJobs.* パッケージ参照を削除し、それらを分離ワーカーと同等のものに置き換えます。

問題: バインド属性が見つかりません

症状:The type or namespace 'QueueTrigger' could not be found

ソリューション: 適切な拡張パッケージを追加し、ステートメントを使用して更新します。

// Add using statement
using Microsoft.Azure.Functions.Worker;

// Install package
// dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues

問題: IDurableOrchestrationContext が見つかりません

症状:The type or namespace 'IDurableOrchestrationContext' could not be found

ソリューション:TaskOrchestrationContextに置き換えます。

using Microsoft.DurableTask;

[Function(nameof(MyOrchestrator))]
public static async Task MyOrchestrator([OrchestrationTrigger] TaskOrchestrationContext context)
{
    // ...
}

問題: JSON シリアル化の違い

症状: シリアル化エラーまたは予期しないデータ形式

ソリューション: 分離モデルでは、既定で System.Text.Json が使用されます。 Program.csでシリアル化を構成します。

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services => {
        services.Configure<JsonSerializerOptions>(options => {
            options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        });
    })
    .Build();

代わりに Newtonsoft.Json を使用するには:

services.Configure<WorkerOptions>(options => {
    options.Serializer = new NewtonsoftJsonObjectSerializer();
});

問題: カスタム シリアル化設定の移行

症状: インプロセス モデルで IMessageSerializerSettingsFactory を使用しており、分離ワーカーで同等のものが必要です。

ソリューション:Program.csでワーカー レベルのシリアライザーを構成します。 詳細については、API リファレンスの behavioral changes のセクション Durable Functions での階層化と永続化を参照してください。

カスタム設定で Newtonsoft.Json を使用するには:

// Program.cs
var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services =>
    {
        services.Configure<WorkerOptions>(options =>
        {
            var settings = new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.None,
                DateFormatHandling = DateFormatHandling.IsoDateFormat,
            };
            options.Serializer = new NewtonsoftJsonObjectSerializer(settings);
        });
    })
    .Build();

この方法では、Newtonsoft.Json および Azure.Core.Serialization NuGet パッケージが必要です。

Checklist

完全な移行を確認するには、次のチェックリストを使用します。

  • プロジェクト ファイルを<OutputType>Exe</OutputType>で更新しました
  • Microsoft.NET.Sdk.Functions をワーカー パッケージに置き換えた
  • Microsoft.Azure.WebJobs.Extensions.DurableTask を分離パッケージに置き換えました
  • ホスト構成を使用してProgram.csを作成しました
  • クラス FunctionsStartup 削除 (存在する場合)
  • すべての [FunctionName][Function] に更新しました
  • IDurableOrchestrationContextに置き換えられましたTaskOrchestrationContext
  • IDurableOrchestrationClientに置き換えられましたDurableTaskClient
  • ログの更新を行い、DI または FunctionContext を使用する
  • local.settings.json ランタイムを使用してdotnet-isolatedを更新しました
  • すべての Microsoft.Azure.WebJobs.* using ステートメントを削除した
  • Microsoft.Azure.Functions.Worker using ステートメントを追加した
  • CreateEntityProxy<T>を直接CallEntityAsync/SignalEntityAsync呼び出しに置き換えた
  • 使用中の場合はタスクハブ間での操作オーバーロードを置き換えました
  • バッチ GetStatusAsync/PurgeInstanceHistoryAsync by-ID呼び出しをフィルターベースまたは個別の呼び出しに置き換えた
  • DurableOrchestrationStatus.History アクセスが GetOrchestrationHistoryAsync への移行
  • DI を使用するようにエンティティ DispatchAsync コンストラクターパラメーターを更新しました
  • すべての関数をローカルでテストしました
  • ステージング スロットにデプロイされ、検証済み
  • 運用環境に切り替えた

次のステップ