Migrieren Sie Ihre Durable Functions App vom In-Process-Modell zum isolierten Workermodell (.NET)

Dieses Handbuch führt Sie durch die Migration Ihrer .NET Durable Functions-App vom In-Process-Modell zum isolierten Workermodell. Die Unterstützung für das In-Process-Modell endet am 10. November 2026. Nach diesem Datum werden keine Sicherheitsupdates oder Fehlerbehebungen bereitgestellt. Mit dem isolierten Arbeitsmodell erhalten Sie außerdem vollständige Prozesskontrolle, Standard-.NET Abhängigkeitsinjektion und Zugriff auf die neuesten Plattformfeatures.

Warnung

Die Unterstützung für das In-Process-Modell endet am 10. November 2026. Es wird empfohlen, jetzt zu migrieren. Hintergrundinformationen zum isolierten Arbeitsmodell finden Sie unter .NET Übersicht über isolierten Arbeitsprozess.

Migrationscheckliste

Verwenden Sie die folgende Checkliste, um den Fortschritt in jedem Migrationsschritt nachzuverfolgen:

Step Abschnitt
1. Überprüfen der Voraussetzungen Voraussetzungen
2. Aktualisieren der Projektdatei Aktualisieren der Projektdatei
3. Hinzufügen von Program.cs Hinzufügen von Program.cs
4. Aktualisieren von Paketreferenzen Aktualisieren von Paketverweise
5. Funktionscode aktualisieren Funktionscode aktualisieren
6. Aktualisieren Sie die local.settings.json Aktualisieren von local.settings.json
7. Lokal testen Lokal testen
8. Bereitstellen für Azure Deploy to Azure

Voraussetzungen

  • Azure Functions Core Tools v4.x oder höher
  • .NET 8.0 SDK (oder Die Zielversion .NET)
  • Visual Studio 2022 oder VS-Code mit Azure Functions Erweiterung

Identifizieren von Zu migrierenden Apps (optional)

Wenn Sie nicht sicher sind, welche Apps weiterhin das In-Process-Modell verwenden, führen Sie dieses Azure PowerShell Skript aus:

$FunctionApps = Get-AzFunctionApp

$AppInfo = @{}

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

$AppInfo

Apps, die dotnet als Laufzeit verwenden, nutzen das In-Process-Modell. Apps, die dotnet-isolated das isolierte Workermodell bereits verwenden.

Aktualisieren der Projektdatei

Bevor (in-process)

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

Nach (isolierter Mitarbeiter)

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

Die wichtigsten Änderungen wechseln zu einem ausführbaren Ausgabetyp und ersetzen alle Microsoft.Azure.WebJobs.*-Pakete durch ihre Microsoft.Azure.Functions.Worker.* Entsprechungen.

Hinzufügen von Program.cs

Für das isolierte Arbeitsmodell ist ein Program.cs Eingangspunkt erforderlich. Erstellen Sie diese Datei im Projektstamm. Wenn Sie eine FunctionsStartup-Klasse in Startup.cs haben, verschieben Sie die Dienstregistrierungen in den ConfigureServices-Block und löschen Sie 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();

Aktualisieren von Paketverweise

Durable Functions Paketabbildung

In-Process-Paket Isoliertes Worker-Paket
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

Allgemeines Mapping für Erweiterungspakete

In Bearbeitung Eingeschlossener Mitarbeiter
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

Von Bedeutung

Entfernen Sie alle Verweise auf Microsoft.Azure.WebJobs.* Namespaces und Microsoft.Azure.Functions.Extensions aus Ihrem Projekt.

Funktionscode aktualisieren

In diesem Abschnitt werden die Codeänderungen für jeden Durable Functions Typ behandelt. Springen Sie zu dem Abschnitt für die Funktionstypen, die Ihre App verwendet:

Eine vollständige API-nach-API-Zuordnung finden Sie in der API-Referenz.

Namespaceänderungen

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

Funktionsattributeänderungen

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

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

Orchestrator-Funktionsänderungen

Vorher (In-Prozess):

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

Nach (Isolierter Arbeiter):

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

Wichtige Unterschiede

Aspekt In Bearbeitung Isolierter Worker
Kontexttyp IDurableOrchestrationContext TaskOrchestrationContext
Logger ILogger Parameter context.CreateReplaySafeLogger()
Merkmal [FunctionName] [Function]

Änderungen der Aktivitätsfunktion

Vorher (In-Prozess):

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

Nach (Isolierter Arbeiter):

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

Clientfunktionsänderungen

Vorher (In-Prozess):

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

Nach (Isolierter Arbeiter):

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

Clienttypänderungen

In Bearbeitung Eingeschlossener Mitarbeiter
IDurableOrchestrationClient DurableTaskClient
StartNewAsync() ScheduleNewOrchestrationInstanceAsync()
CreateCheckStatusResponse() CreateCheckStatusResponseAsync()
HttpRequest / IActionResult HttpRequestData / HttpResponseData

Änderungen der Wiederholungsrichtlinie

In-Process verwendet RetryOptions mit CallActivityWithRetryAsync. Der isolierte Worker verwendet TaskOptions mit dem Standard CallActivityAsync.

Vorher (In-Prozess):

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

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

Nach (Isolierter Arbeiter):

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

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

Änderungen der Entitätsfunktion

Vorher (In-Prozess):

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

Nach (Isolierter Arbeiter):

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

Unterbrechen von Verhaltensänderungen

Überprüfen Sie diese Änderungen, bevor Sie Ihre migrierte App testen. Die vollständige API-nach-API-Zuordnung finden Sie in der API-Referenz.

Warnung

Die Serialisierung wurde standardmäßig geändert: Der isolierte Worker verwendet System.Text.Json standardmäßig anstelle von Newtonsoft.Json. Wenn Ihre Orchestrierungen komplexe Objekte weitergeben, testen Sie die Serialisierung sorgfältig. Siehe JSON-Serialisierungsunterschiede für Konfigurationsoptionen.

Warnung

ContinueAsNew-Standardänderung: Der Standardwert des Parameters preserveUnprocessedEvents wurde von false (2.x) in true (isoliert) geändert. Wenn Ihre Orchestrierung ContinueAsNew verwendet und davon abhängig ist, dass nicht verarbeitete Ereignisse verworfen werden, übergeben Sie explizit preserveUnprocessedEvents: false.

Hinweis

RestartAsync-Standardänderung: Der restartWithNewInstanceId Parameterstandard wurde von true (2.x) in false (isoliert) geändert. Wenn Ihr Code RestartAsync aufruft und von einer neuen, generierten Instanz-ID abhängt, übergeben Sie restartWithNewInstanceId: true explizit.

Weitere wichtige Änderungen:

  • Entitätsproxys entferntCreateEntityProxy<T> ist nicht verfügbar. Verwenden Sie Entities.CallEntityAsync oder Entities.SignalEntityAsync direkt.
  • Auftragübergreifende Vorgänge wurden entfernt – Überladungen, die taskHubName/connectionName akzeptierten, sind nicht verfügbar. Es werden nur Vorgänge desselben Task-Hubs unterstützt.
  • Der Orchestrierungsverlauf wurde verschobenDurableOrchestrationStatus.History ist nicht mehr auf dem Statusobjekt. Verwenden Sie DurableTaskClient.GetOrchestrationHistoryAsync.

Aktualisieren von local.settings.json

Die Schlüsseländerung ist das Setzen von FUNCTIONS_WORKER_RUNTIME von dotnet auf dotnet-isolated.

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

Hinweis

Ihre Speicher-Back-End-Konfiguration (Azure Storage, MSSQL, Netherite oder Durable Task Scheduler) ist von der Migration unverändert. Behalten Sie Ihre vorhandenen Speichereinstellungen bei.

Lokales Testen

Führen Sie Ihre Funktions-App lokal aus und prüfen Sie, dass alle Orchestrierungen, Aktivitäten und Entitäten korrekt funktionieren.

func start

Überprüfen der Funktionalität

Testen Sie die folgenden Szenarien, falls zutreffend:

  1. Starten einer Orchestrierung mit einem HTTP-Trigger
  2. Überwachen des Orchestrierungsstatus
  3. Überprüfen der Ausführungsreihenfolge der Aktivität
  4. Testen von Entitätsvorgängen, falls zutreffend
  5. Überprüfen Sie die Telemetrie von Application Insights

In Azure bereitstellen

Verwenden Sie Bereitstellungsplätze, um Ausfallzeiten zu minimieren:

  1. Erstellen Sie einen Staging-Slot für Ihre Funktions-App.
  2. Konfiguration des Stagingplatzes aktualisieren:
    • Setzen Sie FUNCTIONS_WORKER_RUNTIME auf dotnet-isolated.
    • Aktualisieren Sie die .NET-Stackversion falls erforderlich.
  3. Stellen Sie migrierten Code im Staging-Slot bereit.
  4. Testen Sie gründlich im Stagingslot.
  5. Durchführen eines Slottauschs um Änderungen in die Produktion zu übertragen.

Aktualisieren von Anwendungseinstellungen

Im Azure Portal oder über CLI:

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

Aktualisierung der Stapelkonfiguration

Wenn sie auf eine andere .NET Version ausgerichtet sind:

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

Häufige Probleme bei der Migration

Problem: Assemblyladefehler

Symptom:Could not load file or assembly Fehler.

Lösung: Stellen Sie sicher, dass Sie alle Microsoft.Azure.WebJobs.*-Paketverweise entfernen und durch isolierte Workerentsprechungen ersetzen.

Problem: Das Bindungsattribut wurde nicht gefunden.

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

Lösung: Fügen Sie das entsprechende Erweiterungspaket hinzu, und aktualisieren Sie mithilfe von Anweisungen:

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

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

Problem: IDurableOrchestrationContext nicht gefunden

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

Lösung: Ersetzen durch TaskOrchestrationContext:

using Microsoft.DurableTask;

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

Problem: Unterschiede bei der JSON-Serialisierung

Symptom: Serialisierungsfehler oder unerwartete Datenformate

Lösung: Das isolierte Modell verwendet System.Text.Json standardmäßig. Serialisierung konfigurieren in Program.cs:

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

So verwenden Sie stattdessen Newtonsoft.Json:

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

Problem: Migrieren von benutzerdefinierten Serialisierungseinstellungen

Symptom: Sie haben das In-Process-Modell verwendet IMessageSerializerSettingsFactory und benötigen das Äquivalent in isolierten Workern.

Lösung: Konfigurieren Sie den Serialisierer auf Arbeitsebene in Program.cs. Ausführliche Informationen finden Sie im Abschnitt behavioral changes der API-Referenz und Serialisierung und Persistenz in Durable Functions.

So verwenden Sie Newtonsoft.Json mit benutzerdefinierten Einstellungen:

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

Hinweis

Für diesen Ansatz sind die Newtonsoft.Json und Azure.Core.Serialization NuGet-Pakete erforderlich.

Prüfliste

Verwenden Sie diese Checkliste, um eine vollständige Migration sicherzustellen:

  • Aktualisierte Projektdatei mit <OutputType>Exe</OutputType>
  • Microsoft.NET.Sdk.Functions durch Arbeitspakete ersetzt
  • Microsoft.Azure.WebJobs.Extensions.DurableTask durch isoliertes Paket ersetzt
  • Erstellung von Program.cs mit Hostkonfiguration
  • Klasse FunctionsStartup entfernt (sofern vorhanden)
  • Alle [FunctionName] aktualisiert auf [Function]
  • Ersetzt IDurableOrchestrationContext durch TaskOrchestrationContext
  • Ersetzt IDurableOrchestrationClient durch DurableTaskClient
  • Aktualisierte Protokollierung für die Verwendung von DI oder FunctionContext
  • Aktualisierung von local.settings.json mit dotnet-isolated-Laufzeit
  • Alle Microsoft.Azure.WebJobs.* Verwendungsanweisungen entfernt
  • Microsoft.Azure.Functions.Worker-Verwendungsanweisungen hinzugefügt
  • Ersetzt CreateEntityProxy<T> durch direkte CallEntityAsync/SignalEntityAsync Anrufe
  • Ersetzte Cross-Task-Hub-Betriebsüberladungen (falls verwendet)
  • Ersetzung von GetStatusAsync/PurgeInstanceHistoryAsync-Batchaufrufen nach ID durch filterbasierte oder individuelle Aufrufe
  • Migrierter DurableOrchestrationStatus.History Zugriff auf GetOrchestrationHistoryAsync
  • Aktualisierte Entitätskonstruktorparameter DispatchAsync für die Verwendung von DI
  • Lokales Testen aller Funktionen
  • Bereitgestellt für Staging-Slot und überprüft
  • In die Produktion überführt

Nächste Schritte