Migrace aplikace Durable Functions z procesu do izolovaného pracovního modelu (.NET)

Tato příručka vás provede migrací aplikace .NET Durable Functions z modelu v procesu na izolovaný pracovní model. Model v procesu dosáhne konce podpory 10. listopadu 2026. Po tomto datu nejsou k dispozici žádné aktualizace zabezpečení ani opravy chyb. Model izolovaného pracovního procesu také poskytuje úplné řízení procesů, standardní .NET injektáž závislostí a přístup k nejnovějším funkcím platformy.

Kontrolní seznam pro migraci

Pomocí následujícího kontrolního seznamu můžete sledovat průběh jednotlivých kroků migrace:

Krok Oddíl
1. Ověření požadavků Prerequisites
2. Aktualizace souboru projektu Aktualizace souboru projektu
3. Přidejte Program.cs Přidat Program.cs
4. Aktualizace odkazů na balíčky Aktualizace odkazů na balíčky
5. Aktualizace kódu funkce Aktualizace kódu funkce
6. Aktualizace local.settings.json Aktualizace local.settings.json
7. Místní testování Místní testování
8. Nasazení do Azure Nasazení do Azure

Předpoklady

  • Azure Functions Core Tools verze 4.x nebo novější
  • .NET 8.0 SDK (nebo cílovou .NET verzi)
  • Visual Studio 2022 nebo VS Code with Azure Functions extension

Identifikace aplikací, které se mají migrovat (volitelné)

Pokud si nejste jistí, které aplikace stále používají model v procesu, spusťte tento Azure PowerShell skript:

$FunctionApps = Get-AzFunctionApp

$AppInfo = @{}

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

$AppInfo

Aplikace, které zobrazují dotnet jako runtime, používají model v paměti procesu. Aplikace, které už zobrazují dotnet-isolated , používají izolovaný model pracovního procesu.

Aktualizace souboru projektu

Předchozí (během zpracování)

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

Po izolovaném pracovníkovi

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

Hlavní změny zahrnují přechod na typ výstupu spustitelného souboru a nahrazení všech balíčků Microsoft.Azure.WebJobs.* jejich ekvivalenty Microsoft.Azure.Functions.Worker.*.

Přidejte Program.cs

Izolovaný model pracovního procesu vyžaduje Program.cs vstupní bod. Vytvořte tento soubor v kořenovém adresáři projektu. Pokud máte třídu FunctionsStartup v Startup.cs, přesuňte tyto registrace služeb do bloku ConfigureServices a odstraňte 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();

Aktualizace odkazů na balíčky

mapování balíčků Durable Functions

Balíček v procesu Izolovaný pracovní balíček
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

Běžné mapování balíčků rozšíření

Zpracovává se Izolovaný pracovník
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

Odeberte všechny odkazy na obory názvů Microsoft.Azure.WebJobs.* a Microsoft.Azure.Functions.Extensions z projektu.

Aktualizace kódu funkce

Tato část popisuje změny kódu pro každý typ Durable Functions. Přejděte do části pro typy funkcí, které vaše aplikace používá:

Kompletní mapování rozhraní API podle rozhraní API najdete v referenčních informacích k rozhraní API.

Změny jmenného prostoru

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

Změny atributů funkce

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

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

Změny funkce orchestratoru

Před (Ve zpracování):

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

Za (izolovaný pracovník):

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

Hlavní rozdíly

Aspect V procesu Izolovaný pracovník
Typ kontextu IDurableOrchestrationContext TaskOrchestrationContext
Logger ILogger Parametr context.CreateReplaySafeLogger()
Atribut [FunctionName] [Function]

Změny funkce aktivity

Před (Ve zpracování):

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

Za (izolovaný pracovník):

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

Změny funkce klienta

Před (Ve zpracování):

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

Za (izolovaný pracovník):

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

Změny typu klienta

Zpracovává se Izolovaný pracovník
IDurableOrchestrationClient DurableTaskClient
StartNewAsync() ScheduleNewOrchestrationInstanceAsync()
CreateCheckStatusResponse() CreateCheckStatusResponseAsync()
HttpRequest / IActionResult HttpRequestData / HttpResponseData

Změny zásad opakování

In-process používá RetryOptions s CallActivityWithRetryAsync. Izolovaný pracovník používá TaskOptions se standardním CallActivityAsync.

Před (Ve zpracování):

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

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

Za (izolovaný pracovník):

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

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

Změny funkce entity

Před (Ve zpracování):

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

Za (izolovaný pracovník):

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

Zásadní změny chování

Před otestem migrované aplikace si tyto změny projděte. Kompletní mapování rozhraní API podle rozhraní API najdete v referenčních informacích k rozhraní API.

Warning

Změna výchozího nastavení serializace: Izolovaný pracovní proces používá System.Text.Json ve výchozím nastavení místo Newtonsoft.Json. Pokud vaše orchestrace předávají složité objekty, důkladně otestujte serializaci. Možnosti konfigurace najdete v rozdílech serializace JSON .

Warning

ContinueAsNew default change: Výchozí hodnota parametru preserveUnprocessedEvents byla změněna z false (2.x) na true (isolated). Pokud orchestrace používá ContinueAsNew a spoléhá na to, že nezpracované události mají být zahozeny, explicitně předejte preserveUnprocessedEvents: false.

Note

Výchozí změna restartAsync: Výchozí restartWithNewInstanceId parametr se změnil z true (2.x) na false (izolovaný). Pokud váš kód volá RestartAsync a závisí na vygenerovaném novém ID instance, explicitně předejte restartWithNewInstanceId: true.

Další velmi vhodné změny:

  • Proxy entity byly odebrányCreateEntityProxy<T> není k dispozici. Použijte Entities.CallEntityAsync nebo Entities.SignalEntityAsync přímo.
  • Operace mezi úlohami rozbočovače byly odebrány – přetížení, která přijímala taskHubName/connectionName , nejsou k dispozici. Podporují se pouze operace stejného centra úloh.
  • Historie orchestrace byla přesunutaDurableOrchestrationStatus.History už není na objektu stavu. Použijte DurableTaskClient.GetOrchestrationHistoryAsync.

Aktualizace local.settings.json

Zásadní změnou je nastavení FUNCTIONS_WORKER_RUNTIME z dotnet na dotnet-isolated.

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

Note

Migrace nemění konfiguraci back-endu úložiště (Azure Storage, MSSQL, Netherite nebo Durable Task Scheduler). Ponechte stávající nastavení související s úložištěm.

Místní testování

Spusťte aplikaci funkcí místně a ověřte, že všechny orchestrace, aktivity a entity fungují správně.

func start

Ověření funkčnosti

Podle potřeby otestujte následující scénáře:

  1. Spuštění orchestrace pomocí triggeru HTTP
  2. Monitorování stavu orchestrace
  3. Ověření pořadí provádění aktivit
  4. Otestujte operace entit, pokud je to vhodné
  5. Zkontrolovat telemetrii Application Insights

Nasazení do Azure

K minimalizaci výpadků použijte sloty nasazení:

  1. Vytvořte přípravný slot pro vaši funkční aplikaci.
  2. Aktualizace konfigurace staging slotu:
    • Nastavte FUNCTIONS_WORKER_RUNTIME na hodnotu dotnet-isolated.
    • V případě potřeby aktualizujte verzi .NET zásobníku.
  3. Nasaďte migrovaný kód do přípravného slotu.
  4. Důkladně otestujte v testovací fázi.
  5. Proveďte prohození slotů a přesuňte změny do produkčního prostředí.

Aktualizace nastavení aplikace

Na portálu Azure nebo prostřednictvím rozhraní příkazového řádku:

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

Aktualizace konfigurace stacku

Pokud cílíte na jinou verzi .NET:

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

Běžné problémy s migrací

Problém: Chyby načtení sestavení

Příznaky:Could not load file or assembly Chyby.

Solution: Ujistěte se, že odeberete všechny odkazy na balíčky Microsoft.Azure.WebJobs.* a nahradíte je izolovanými pracovními ekvivalenty.

Problém: Atribut vazby nebyl nalezen.

Příznak:The type or namespace 'QueueTrigger' could not be found

Řešení: Přidejte příslušný balíček rozšíření a aktualizujte příkazy using:

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

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

Problém: IDurableOrchestrationContext se nenašel

Příznak:The type or namespace 'IDurableOrchestrationContext' could not be found

Řešení: Nahradit:TaskOrchestrationContext

using Microsoft.DurableTask;

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

Problém: Rozdíly serializace JSON

Příznakem: Chyby serializace nebo neočekávané formáty dat

Řešení: Izolovaný model používá System.Text.Json ve výchozím nastavení. Konfigurace serializace v Program.cs:

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

Pokud chcete místo toho použít Newtonsoft.Json:

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

Problém: Migrace vlastního nastavení serializace

Příznak: Použili jste IMessageSerializerSettingsFactory v modelu v rámci procesu a potřebujete ekvivalent v izolovaném workeru.

Řešení: Nakonfigurujte serializátor na úrovni pracovníka v Program.cs. Pro podrobnosti se podívejte na část změny chování referenční dokumentace k rozhraní API a Serializace a perzistence v Durable Functions.

K použití Newtonsoft.Json s vlastním nastavením:

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

Tento přístup vyžaduje balíčky NuGet Newtonsoft.Json a Azure.Core.Serialization.

Kontrolní seznam

K zajištění úplné migrace použijte tento kontrolní seznam:

  • Aktualizovaný soubor projektu pomocí <OutputType>Exe</OutputType>
  • Nahrazeno Microsoft.NET.Sdk.Functions pracovními balíčky
  • Nahrazeno Microsoft.Azure.WebJobs.Extensions.DurableTask izolovaným balíčkem
  • Vytvořeno Program.cs s konfigurací hostitelského systému
  • FunctionsStartup Odebrání třídy (pokud je k dispozici)
  • Aktualizováno vše [FunctionName] na [Function]
  • Nahrazeno IDurableOrchestrationContext za TaskOrchestrationContext
  • Nahrazeno IDurableOrchestrationClient za DurableTaskClient
  • Aktualizované protokolování tak, aby používalo DI nebo FunctionContext
  • Aktualizováno local.settings.json modulem dotnet-isolated runtime
  • Odebrali se všechny příkazy Microsoft.Azure.WebJobs.* using
  • Přidání příkazů Microsoft.Azure.Functions.Worker using
  • Nahrazeno CreateEntityProxy<T> přímými CallEntityAsync/SignalEntityAsync voláními
  • Nahrazení přetížení operací mezi uzly úloh (pokud se používá)
  • Dávkové volání GetStatusAsync/PurgeInstanceHistoryAsync podle ID bylo nahrazeno voláními na základě filtru nebo jednotlivými voláními.
  • Migrovaný DurableOrchestrationStatus.History přístup k GetOrchestrationHistoryAsync
  • Aktualizace parametrů konstruktoru entity DispatchAsync tak, aby používaly DI
  • Místní otestování všech funkcí
  • Nasazené do přípravného slotu a ověřené
  • Prohozeno do produkčního prostředí

Další kroky