Migre a sua aplicação Durable Functions do modelo em processo para o modelo de trabalhador isolado (.NET)

Este guia guia-o na migração da sua aplicação de .NET Durable Functions do modelo em processo para o modelo de trabalhador isolado. O modelo em processo chega ao fim do suporte a 10 de novembro de 2026. Após essa data, não são fornecidas atualizações de segurança nem correções de bugs. O modelo de trabalhador isolado também lhe dá controlo total de processos, injeção padrão de dependência .NET e acesso às funcionalidades mais recentes da plataforma.

Advertência

O suporte ao modelo em processo termina a 10 de novembro de 2026. Recomendamos migrar agora. Para contexto sobre o modelo do trabalhador isolado, veja .NET visão geral do processo do trabalhador isolado.

Lista de verificação da migração

Use a seguinte lista de verificação para acompanhar o seu progresso em cada etapa da migração:

Step Seção
1. Verificar os pré-requisitos Prerequisites
2. Atualizar o ficheiro do projeto Atualizar o ficheiro do projeto
3. Adicionar Program.cs Adicionar Program.cs
4. Atualizar referências de pacotes Atualizar referências de pacotes
5. Atualizar código de função Atualizar código de função
6. Atualizar local.settings.json Atualizar local.settings.json
7. Testar localmente Testar localmente
8. Implementar no Azure Implementar no Azure

Pré-requisitos

  • Funções do Azure Core Tools v4.x ou posterior
  • .NET 8.0 SDK (ou a tua versão .NET alvo)
  • Visual Studio 2022 ou VS Code com Funções do Azure extensão

Identificar aplicações a migrar (opcional)

Se não tiveres a certeza de que aplicações ainda usam o modelo em processo, executa este script Azure PowerShell:

$FunctionApps = Get-AzFunctionApp

$AppInfo = @{}

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

$AppInfo

As aplicações que mostram como tempo de execução utilizam o modelo em processo. As aplicações que aparecem dotnet-isolated já usam o modelo de trabalhador isolado.

Atualizar o arquivo de projeto

Antes (em processo)

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

Depois (trabalhador isolado)

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

As principais alterações são a mudança para um tipo de saída executável e a substituição de todos os pacotes Microsoft.Azure.WebJobs.* pelos seus equivalentes Microsoft.Azure.Functions.Worker.*.

Adicionar Program.cs

O modelo de trabalhador isolado requer um Program.cs ponto de entrada. Cria este ficheiro na raiz do teu projeto. Se tiver uma FunctionsStartup classe em Startup.cs, mova essas registos de serviço para o bloco ConfigureServices e elimina 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();

Atualizar referências de pacotes

Mapeamento de pacotes das Durable Functions

Pacote em andamento Pacote de trabalhadores isolados
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

Mapeamento comum de pacotes de extensões

Em andamento Trabalhador isolado
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

Importante

Remova quaisquer referências a Microsoft.Azure.WebJobs.* namespaces e Microsoft.Azure.Functions.Extensions do seu projeto.

Atualizar código de função

Esta secção cobre as alterações de código para cada tipo de Durable Functions. Vai para a secção dos tipos de funções que a tua aplicação utiliza:

Para um mapeamento completo API a API, consulte a referência da API.

Alterações no espaço de nomes

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

Alterações no atributo da função

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

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

Alterações na função do orquestrador

Antes (In-Process):

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

Depois (Trabalhador Isolado):

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

Diferenças principais

Aspeto Em Processo Trabalhador Isolado
Tipo de contexto IDurableOrchestrationContext TaskOrchestrationContext
Logger parâmetro ILogger context.CreateReplaySafeLogger()
Attribute [FunctionName] [Function]

Alterações na função de atividade

Antes (In-Process):

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

Depois (Trabalhador Isolado):

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

Alterações nas funções do cliente

Antes (In-Process):

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

Depois (Trabalhador Isolado):

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

Alterações no tipo de cliente

Em andamento Trabalhador isolado
IDurableOrchestrationClient DurableTaskClient
StartNewAsync() ScheduleNewOrchestrationInstanceAsync()
CreateCheckStatusResponse() CreateCheckStatusResponseAsync()
HttpRequest / IActionResult HttpRequestData / HttpResponseData

Alterações na política de novas tentativas

O processo utiliza RetryOptions com CallActivityWithRetryAsync. O trabalhador isolado usa TaskOptions com o padrão CallActivityAsync.

Antes (In-Process):

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

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

Depois (Trabalhador Isolado):

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

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

Alterações na função da entidade

Antes (In-Process):

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

Depois (Trabalhador Isolado):

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

Alterações de comportamento disruptivas

Revise estas alterações antes de testar a sua aplicação migrada. Para o mapeamento completo API-a-API, consulte a referência da API.

Advertência

Alteração do padrão de serialização: O trabalhador isolado usa System.Text.Json por padrão em vez de Newtonsoft.Json. Se as suas orquestrações passarem por objetos complexos, teste a serialização cuidadosamente. Consulte diferenças de serialização JSON para opções de configuração.

Advertência

ContinueAsNew mudança padrão: O preserveUnprocessedEvents parâmetro padrão mudou de false (2.x) para true (isolado). Se a sua orquestração usar ContinueAsNew e depender de que eventos não processados sejam descartados, passe preserveUnprocessedEvents: false explicitamente.

Observação

Alteração do padrão RestartAsync: O restartWithNewInstanceId padrão do parâmetro mudou de true (2.x) para false (isolado). Se o seu código chamar RestartAsync e depender de ser gerado um novo ID de instância, passe explicitamente restartWithNewInstanceId: true.

Outras alterações notáveis:

  • Proxies de entidade removidosCreateEntityProxy<T> não está disponível. Use Entities.CallEntityAsync ou Entities.SignalEntityAsync diretamente.
  • Operações de cross-task-hub removidas — Sobrecargas que aceitavam taskHubName/connectionName não estão disponíveis. Apenas operações no mesmo centro de tarefas são suportadas.
  • Histórico da orquestração transferidoDurableOrchestrationStatus.History agora não está no objeto de estado. Utilize DurableTaskClient.GetOrchestrationHistoryAsync.

Atualize o ficheiro local.settings.json

A mudança de chave é definir FUNCTIONS_WORKER_RUNTIME de dotnet para dotnet-isolated:

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

Observação

A configuração do seu backend de armazenamento (Armazenamento do Azure, MSSQL, Netherite ou Durable Task Scheduler) mantém-se inalterada com a migração. Mantém as definições existentes relacionadas com o armazenamento.

Testar localmente

Executa a tua aplicação de funções localmente e verifica se todas as orquestrações, atividades e entidades funcionam corretamente.

func start

Verificar funcionalidade

Teste os seguintes cenários conforme aplicável:

  1. Inicie uma orquestração com um gatilho HTTP
  2. Monitorizar o estado da orquestração
  3. Verificar a ordem de execução da atividade
  4. Testar operações da entidade, se aplicável
  5. Verifique a telemetria do Application Insights

Publicar no Azure

Use slots de implementação para minimizar o tempo de inatividade.

  1. Cria um slot de preparação para a sua aplicação de função.
  2. Atualizar configuração do slot de preparação:
    • Defina FUNCTIONS_WORKER_RUNTIME como dotnet-isolated.
    • Atualize a versão da pilha .NET se necessário.
  3. Implementa o código migrado para o slot de staging.
  4. Teste cuidadosamente no slot de preparação.
  5. Realiza a troca de slot para transferir alterações para a produção.

Atualizar configurações do aplicativo

No portal Azure ou via CLI:

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

Atualizar a configuração da pilha

Se pretender direcionar para uma versão diferente do .NET:

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

Problemas comuns de migração

Problema: Erros de carga de montagem

Sintoma:Could not load file or assembly erros.

Solução: Certifique-se de remover todas as referências de pacotes Microsoft.Azure.WebJobs.* e substituí-las por equivalentes de trabalhadores isolados.

Questão: Atributo de ligação não encontrado

Sintoma:The type or namespace 'QueueTrigger' could not be found

Solução: Adicione o pacote de extensão apropriado e atualize usando instruções:

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

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

Problema: IDurableOrchestrationContext não encontrado

Sintoma:The type or namespace 'IDurableOrchestrationContext' could not be found

Solução: Substituir por TaskOrchestrationContext:

using Microsoft.DurableTask;

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

Questão: Diferenças na serialização JSON

Sintoma: Erros de serialização ou formatos de dados inesperados

Solução: O modelo isolado usa System.Text.Json por defeito. Configurar serialização em Program.cs:

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

Para usar Newtonsoft.Json em vez disso:

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

Problema: Migração de definições de serialização personalizadas

Sintoma: Usaste IMessageSerializerSettingsFactory no modelo em processo e precisas do equivalente no trabalhador isolado.

Solução: Configure o serializador do nível do trabalhador em Program.cs. Para detalhes, consulte a secção alterações comportamentais da referência da API e Serialização e persistência em Durable Functions.

Para usar Newtonsoft.Json com definições personalizadas:

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

Observação

Esta abordagem requer os pacotes NuGet Newtonsoft.Json e Azure.Core.Serialization.

Lista de Verificação

Use esta lista de verificação para garantir uma migração completa:

  • Ficheiro de projeto atualizado com <OutputType>Exe</OutputType>
  • Substituíram Microsoft.NET.Sdk.Functions por pacotes de trabalhadores
  • Substituiu Microsoft.Azure.WebJobs.Extensions.DurableTask por pacote isolado
  • Criado Program.cs com a configuração do anfitrião
  • Classe removida FunctionsStartup (se presente)
  • Atualizado tudo [FunctionName] para [Function]
  • Substituído IDurableOrchestrationContext por TaskOrchestrationContext
  • Substituído IDurableOrchestrationClient por DurableTaskClient
  • Registo atualizado para usar DI ou FunctionContext
  • Atualizado local.settings.json com dotnet-isolated tempo de execução
  • Removi todas as instruções Microsoft.Azure.WebJobs.* usando instruções
  • Adicionado Microsoft.Azure.Functions.Worker usando declarações
  • Substituída CreateEntityProxy<T> por chamadas diretas CallEntityAsync/SignalEntityAsync
  • Foram substituídas as sobrecargas de operação do hub entre tarefas (se usadas)
  • Substituiu as chamadas de lote por-ID GetStatusAsync/PurgeInstanceHistoryAsync por chamadas baseadas em filtro ou individuais
  • O acesso foi migrado de DurableOrchestrationStatus.History para GetOrchestrationHistoryAsync
  • Atualizei os parâmetros do construtor de entidade DispatchAsync para usar DI
  • Testava todas as funções localmente
  • Implantado no slot de pré-produção e verificado
  • Transferido para produção

Passos seguintes