Migrer votre application Durable Functions du processus vers un modèle worker isolé (.NET)

Ce guide vous guide tout au long de la migration de votre application .NET Durable Functions du modèle in-process vers le modèle worker isolé. Le modèle in-process atteint la fin du support le 10 novembre 2026. Après cette date, aucune mise à jour de sécurité ni correctifs de bogues n’est fourni. Le modèle worker isolé vous offre également un contrôle de processus complet, une injection de dépendances de .NET standard et un accès aux dernières fonctionnalités de la plateforme.

Avertissement

La prise en charge du modèle in-process se termine le 10 novembre 2026. Nous vous recommandons de migrer maintenant. Pour plus d’informations sur le modèle worker isolé, consultez vue d’ensemble du processus worker isolé .NET.

Liste des éléments à vérifier pour la migration

Utilisez la liste de contrôle suivante pour suivre votre progression à chaque étape de migration :

Étape Section
1. Vérifier les prérequis Conditions préalables
2. Mettre à jour le fichier projet Mettre à jour le fichier projet
3. Ajouter Program.cs Ajouter le fichier Program.cs
4. Mettre à jour les références de package Mettre à jour les références de package
5. Mettre à jour le code de fonction Mettre à jour le code de fonction
6. Mettre à jour local.settings.json Mettre à jour local.settings.json
7. Tester localement Tester localement
8. Déployer sur Azure Déployer dans Azure

Prerequisites

  • Azure Functions Core Tools v4.x ou version ultérieure
  • .NET sdk 8.0 (ou votre version .NET cible)
  • Visual Studio 2022 ou VS Code avec Azure Functions extension

Identifier les applications à migrer (facultatif)

Si vous ne savez pas quelles applications utilisent toujours le modèle in-process, exécutez ce script Azure PowerShell :

$FunctionApps = Get-AzFunctionApp

$AppInfo = @{}

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

$AppInfo

Les applications qui s’affichent dotnet en tant que runtime utilisent le modèle in-process. Applications qui montrent dotnet-isolated utilisent déjà le modèle worker isolé.

Mettre à jour le fichier projet

Avant (en cours)

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

Après (travailleur isolé)

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

Les principales modifications passent à un type de sortie exécutable et remplacent tous les packages Microsoft.Azure.WebJobs.* par leurs équivalents Microsoft.Azure.Functions.Worker.*.

Ajouter Program.cs

Le modèle d'ouvrier isolé nécessite un point d'entrée Program.cs. Créez ce fichier à la racine de votre projet. Si vous avez une FunctionsStartup classe dans Startup.cs, déplacez ces enregistrements de service dans le bloc ConfigureServices et supprimez 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();

Mettre à jour les références de package

Mappage de paquet Durable Functions

Paquet en cours de traitement Paquet de travailleur isolé
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

Mappage commun des packages d’extension

En cours Travailleur isolé
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

Supprimez les références aux espaces de noms Microsoft.Azure.WebJobs.* et Microsoft.Azure.Functions.Extensions de votre projet.

Mettre à jour le code de fonction

Cette section traite des modifications de code pour chaque type de Durable Functions. Accédez à la section pour les types de fonctions que votre application utilise :

Pour obtenir un mappage d’API par API complet, consultez la référence de l’API.

Modifications de l’espace de noms

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

Modifications de l’attribut de fonction

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

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

Modifications de la fonction d'orchestration

Avant (En cours) :

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

Après (Travailleur isolé) :

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

Principales différences

Aspect En cours Travailleur isolé
Type de contexte IDurableOrchestrationContext TaskOrchestrationContext
Logger Paramètre ILogger context.CreateReplaySafeLogger()
Caractéristique [FunctionName] [Function]

Modifications de la fonction d’activité

Avant (En cours) :

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

Après (Travailleur isolé) :

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

Modifications de la fonction cliente

Avant (En cours) :

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

Après (Travailleur isolé) :

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

Modifications de type client

En cours Travailleur isolé
IDurableOrchestrationClient DurableTaskClient
StartNewAsync() ScheduleNewOrchestrationInstanceAsync()
CreateCheckStatusResponse() CreateCheckStatusResponseAsync()
HttpRequest / IActionResult HttpRequestData / HttpResponseData

Nouvelles tentatives de modification de la stratégie

In-process utilise RetryOptions avec CallActivityWithRetryAsync. Le travailleur isolé utilise TaskOptions avec la norme CallActivityAsync.

Avant (En cours) :

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

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

Après (Travailleur isolé) :

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

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

Modifications de la fonction d’entité

Avant (En cours) :

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

Après (Travailleur isolé) :

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

Changements de comportement cassants

Passez en revue ces modifications avant de tester votre application migrée. Pour obtenir le mappage d’API par API complet, consultez la référence de l’API.

Avertissement

La sérialisation par défaut a changé : le worker isolé utilise System.Text.Json par défaut au lieu de Newtonsoft.Json. Si vos orchestrations passent des objets complexes, testez soigneusement la sérialisation. Consultez les différences de sérialisation JSON pour connaître les options de configuration.

Avertissement

Modification par défaut de ContinueAsNew : le preserveUnprocessedEvents paramètre par défaut est passé de false (2.x) à true (isolé). Si votre orchestration utilise ContinueAsNew et s’appuie sur le fait que les événements non traités soient ignorés, transmettez preserveUnprocessedEvents: false explicitement.

Note

Changement par défaut de RestartAsync : le restartWithNewInstanceId paramètre par défaut est passé de true (2.x) à false (isolé). Si votre code appelle RestartAsync et dépend d’un nouvel ID d’instance généré, passez restartWithNewInstanceId: trueexplicitement .

Autres changements notables :

  • Les proxys d’entité ont été supprimésCreateEntityProxy<T> n'est pas disponible. Utilisez Entities.CallEntityAsync ou Entities.SignalEntityAsync directement.
  • Opérations inter-tâches-hub supprimées : les surcharges acceptées taskHubName/connectionName ne sont pas disponibles. Seules les opérations sur des hubs de tâches identiques sont prises en charge.
  • Historique de l’orchestration déplacé : DurableOrchestrationStatus.History n'est plus inclus dans l'objet d'état. Utilisez DurableTaskClient.GetOrchestrationHistoryAsync.

Mettre à jour local.settings.json

Le changement principal est la configuration de FUNCTIONS_WORKER_RUNTIME, passant de dotnet à dotnet-isolated.

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

Note

Votre configuration back-end de stockage (stockage Azure, MSSQL, Netherite ou Durable Task Scheduler) n’est pas modifiée par la migration. Conservez vos paramètres de stockage existants.

Tester localement

Exécutez votre application de fonction localement et vérifiez que toutes les orchestrations, activités et entités fonctionnent correctement.

func start

Vérifier le fonctionnement

Testez les scénarios suivants, le cas échéant :

  1. Démarrer une orchestration avec un déclencheur HTTP
  2. Surveiller l’état de l’orchestration
  3. Vérifier l’ordre d’exécution de l’activité
  4. Tester les opérations d’entité le cas échéant
  5. Vérifier la télémétrie de Application Insights

Déployer sur Azure

Utilisez des emplacements de déploiement pour réduire les temps d’arrêt :

  1. Créez un emplacement de mise en scène pour votre application de fonction.
  2. Mettre à jour la configuration de l’espace de mise en scène :
    • Affectez la valeur FUNCTIONS_WORKER_RUNTIME à dotnet-isolated.
    • Mettez à jour la version de la pile .NET si nécessaire.
  3. Déployez le code migré sur le slot intermédiaire.
  4. Testez soigneusement dans l’emplacement de préproduction.
  5. Effectuez un échange d’emplacement pour déplacer les modifications vers la production.

Mettre à jour les paramètres de l’application

Dans le portail Azure ou via l’interface CLI :

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

Mettre à jour la configuration de la pile

Si vous ciblez une autre version de .NET :

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

Problèmes courants de migration

Problème : erreurs de chargement d’assembly

Symptôme:Could not load file or assembly Erreurs.

Solution : Veillez à supprimer toutes les références de paquet Microsoft.Azure.WebJobs.* et à les remplacer par des équivalents de travailleur isolés.

Problème : attribut de liaison introuvable

Symptôme:The type or namespace 'QueueTrigger' could not be found

Solution: Ajoutez le package d’extension approprié et mettez à jour à l’aide d’instructions :

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

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

Problème : IDurableOrchestrationContext introuvable

Symptôme:The type or namespace 'IDurableOrchestrationContext' could not be found

Solution: Remplacez par TaskOrchestrationContext:

using Microsoft.DurableTask;

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

Problème : différences de sérialisation JSON

Symptôme: Erreurs de sérialisation ou formats de données inattendus

Solution: Le modèle isolé utilise System.Text.Json par défaut. Configurer la sérialisation dans Program.cs:

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

Pour utiliser Newtonsoft.Json à la place :

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

Problème : Migration des paramètres de sérialisation personnalisés

Symptôme : Vous avez utilisé IMessageSerializerSettingsFactory dans un modèle en cours et avez besoin de l’équivalent dans un travailleur isolé.

Solution: Configurez le sérialiseur de niveau de travailleur dans Program.cs. Pour plus d’informations, consultez la section behavioral changes de la référence d’API et Sérialisation et persistance dans Durable Functions.

Pour utiliser Newtonsoft.Json avec des paramètres personnalisés :

// 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();

Note

Cette approche nécessite les packages NuGet Newtonsoft.Json et Azure.Core.Serialization.

Liste de contrôle

Utilisez cette liste de contrôle pour garantir une migration complète :

  • Fichier projet mis à jour avec <OutputType>Exe</OutputType>
  • Remplacement de Microsoft.NET.Sdk.Functions par des packages de worker
  • Remplacé Microsoft.Azure.WebJobs.Extensions.DurableTask par un package isolé
  • Créé Program.cs avec la configuration de l’hôte
  • Classe supprimée FunctionsStartup (le cas échéant)
  • Mise à jour de tous [FunctionName] vers [Function]
  • Remplacé par IDurableOrchestrationContextTaskOrchestrationContext
  • Remplacé par IDurableOrchestrationClientDurableTaskClient
  • Mise à jour de la journalisation pour utiliser DI ou FunctionContext
  • Mise à jour de local.settings.json avec le runtime dotnet-isolated
  • Suppression de toutes les instructions utilisant Microsoft.Azure.WebJobs.*
  • Ajout des instructions using pour Microsoft.Azure.Functions.Worker
  • ** Remplacé CreateEntityProxy<T> par des appels directs CallEntityAsync/SignalEntityAsync
  • Remplacement des surcharges d’opérations cross-task-hub (si utilisées)
  • Remplacement des appels par lots GetStatusAsync/PurgeInstanceHistoryAsync basés sur l’ID par des appels individuels ou basés sur des filtres
  • Accès migré DurableOrchestrationStatus.History vers GetOrchestrationHistoryAsync
  • Mise à jour des paramètres du constructeur de l’entité DispatchAsync pour utiliser DI
  • Test de toutes les fonctions localement
  • Déploiement sur l’emplacement de préproduction et vérification
  • Passage en production

Étapes suivantes