Condividi tramite


Eseguire la migrazione da In-Process al modello di lavoro isolato

Questa guida illustra come eseguire la migrazione dell'applicazione Durable Functions dal modello in-process al modello di lavoro isolato.

Avviso

Il supporto per il modello in-process termina il 10 novembre 2026. Eseguire la migrazione al modello di lavoro isolato per il supporto continuo e l'accesso alle nuove funzionalità.

Perché eseguire la migrazione?

Fine del supporto per il modello in-process

Microsoft ha annunciato che il modello in-process per .NET Azure Functions raggiunge la fine del supporto il 10 novembre 2026. Dopo questa data:

  • Non vengono forniti aggiornamenti della sicurezza
  • Non vengono rilasciate correzioni di bug
  • Le nuove funzionalità sono disponibili solo nel modello di lavoro isolato

Vantaggi del modello di lavoro isolato

La migrazione al modello di lavoro isolato offre i vantaggi seguenti:

Beneficio Descrizione
Nessun conflitto di assembly Il codice viene eseguito in un processo separato, eliminando i conflitti di versione
Controllo completo del processo Controllare l'avvio, la configurazione e il middleware tramite Program.cs
Modelli di inserimento delle dipendenze standard Utilizzare l'inserimento delle dipendenze familiare di .NET
flessibilità della versione .NET Supporto per LTS, STS e .NET Framework
Supporto del middleware Pipeline completa di middleware ASP.NET Core
Prestazioni migliori integrazione di ASP.NET Core per i trigger HTTP
Supporto della piattaforma Accesso al Piano di Consumo Flessibile e .NET Aspire

Prerequisiti

Prima di avviare la migrazione, assicurarsi di disporre dei prerequisiti seguenti:

  • Azure Functions Core Tools v4.x o versione successiva
  • .NET 8.0 SDK (o versione di .NET di destinazione)
  • Visual Studio 2022 o VS Code with Azure Functions extension
  • Familiarità con i concetti di Durable Functions

Panoramica della migrazione

Il processo di migrazione prevede questi passaggi principali:

  1. Identificare le app di cui eseguire la migrazione
  2. Aggiornare il file di progetto
  3. Aggiungere Program.cs
  4. Aggiornare i riferimenti ai pacchetti
  5. Aggiornare il codice della funzione
  6. Aggiornare local.settings.json
  7. Testare localmente
  8. Deploy in Azure

Identificare le app di cui eseguire la migrazione

Usare questo script di Azure PowerShell per trovare le app di funzione della tua sottoscrizione che usano il modello in-process.

$FunctionApps = Get-AzFunctionApp

$AppInfo = @{}

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

$AppInfo

Le app che mostrano dotnet come runtime usano il modello in-process. Le app che utilizzano dotnet-isolated già usano il modello di lavoro isolato.

Aggiornare il file di progetto

Prima (In-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>

Dopo (Isolated Worker)

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

Modifiche chiave

  • Add <OutputType>Exe</OutputType> - il lavoratore isolato è un eseguibile
  • Aggiungere <FrameworkReference Include="Microsoft.AspNetCore.App" /> - Per l'integrazione di ASP.NET Core
  • Sostituire Microsoft.NET.Sdk.Functions con pacchetti Microsoft.Azure.Functions.Worker.*
  • Sostituire Microsoft.Azure.WebJobs.Extensions.DurableTask con Microsoft.Azure.Functions.Worker.Extensions.DurableTask

Aggiungere Program.cs

Creare un nuovo Program.cs file nella radice del progetto:

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

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services => {
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
    })
    .Build();

host.Run();

Con i servizi personalizzati

Se si dispone di una FunctionsStartup classe, spostare la configurazione in Program.cs:

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

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services => {
        // Application Insights
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
        
        // Your custom services (previously in FunctionsStartup)
        services.AddSingleton<IMyService, MyService>();
        services.AddHttpClient<IApiClient, ApiClient>();
    })
    .Build();

host.Run();

Eliminare FunctionsStartup

Se disponi di Startup.cs con [assembly: FunctionsStartup(...)], eliminalo dopo aver trasferito la configurazione in Program.cs.

Aggiornare i riferimenti ai pacchetti

modifiche al pacchetto Durable Functions

Pacchetto in corso Pacchetto di lavoro isolato
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

Modifiche comuni ai pacchetti di estensione

In corso Lavoro isolato
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

Rimuovere eventuali riferimenti agli spazi dei nomi Microsoft.Azure.WebJobs.* e Microsoft.Azure.Functions.Extensions dal progetto.

Aggiornare il codice della funzione

Modifiche dello spazio dei nomi

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

Modifiche dell'attributo della funzione

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

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

Modifiche delle funzioni dell'agente di orchestrazione

Prima di (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 };
}

Dopo (Lavoratore isolato):

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

Differenze principali

Aspetto In lavorazione Lavoro isolato
Tipo di contesto IDurableOrchestrationContext TaskOrchestrationContext
Logger parametro ILogger context.CreateReplaySafeLogger()
Attribute [FunctionName] [Function]

Modifiche delle funzioni di attività

Prima di (In corso):

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

Dopo (Isolated Worker):

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

Modifiche della funzione client

Prima di (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);
}

Dopo (Isolated Worker):

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

Modifiche al tipo di client

In corso Lavoro isolato
IDurableOrchestrationClient DurableTaskClient
StartNewAsync() ScheduleNewOrchestrationInstanceAsync()
CreateCheckStatusResponse() CreateCheckStatusResponseAsync()
HttpRequest / IActionResult HttpRequestData / HttpResponseData

Modifiche ai criteri di ripetizione dei tentativi

In-process usa RetryOptions con CallActivityWithRetryAsync. Il lavoratore isolato usa TaskOptions con lo standard CallActivityAsync.

Prima di (In-Process):

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

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

Dopo (Isolated Worker):

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

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

Modifiche alle funzioni di entità

Prima di (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;
    }
}

Dopo (Isolated Worker):

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

Informazioni di riferimento complete sulle API

Le tabelle seguenti forniscono una corrispondenza completa tra l'SDK in-process 2.x e le API dell'SDK del worker isolato.

API per il client

In-process (2.x) Lavoro isolato
IDurableOrchestrationClient DurableTaskClient
IDurableOrchestrationClient.StartNewAsync DurableTaskClient.ScheduleNewOrchestrationInstanceAsync
IDurableOrchestrationClient.GetStatusAsync DurableTaskClient.GetInstanceAsync
IDurableOrchestrationClient.ListInstancesAsync DurableTaskClient.GetAllInstancesAsync
IDurableOrchestrationClient.TerminateAsync DurableTaskClient.TerminateInstanceAsync
IDurableOrchestrationClient.SuspendAsync DurableTaskClient.SuspendInstanceAsync
IDurableOrchestrationClient.ResumeAsync DurableTaskClient.ResumeInstanceAsync
IDurableOrchestrationClient.RaiseEventAsync DurableTaskClient.RaiseEventAsync
IDurableOrchestrationClient.RewindAsync DurableTaskClient.RewindInstanceAsync
IDurableOrchestrationClient.RestartAsync DurableTaskClient.RestartAsync
IDurableOrchestrationClient.PurgeInstanceHistoryAsync DurableTaskClient.PurgeInstanceAsync oppure PurgeAllInstancesAsync
IDurableOrchestrationClient.CreateCheckStatusResponse DurableTaskClient.CreateCheckStatusResponseAsync (metodo di estensione, accetta HttpRequestData)
IDurableOrchestrationClient.WaitForCompletionOrCreateCheckStatusResponseAsync DurableTaskClient.WaitForCompletionOrCreateCheckStatusResponseAsync (metodo di estensione, timeout sostituito da CancellationToken)
IDurableOrchestrationClient.CreateHttpManagementPayload DurableTaskClient.CreateHttpManagementPayload (metodo di estensione)
IDurableOrchestrationClient.MakeCurrentAppPrimaryAsync Rimosso
IDurableOrchestrationClient.GetStatusAsync(IEnumerable<string>) Rimosso. Utilizzare GetInstanceAsync in un ciclo iterativo o GetAllInstancesAsync con un filtro di query.
IDurableOrchestrationClient.PurgeInstanceHistoryAsync(IEnumerable<string>) Rimosso. Usare PurgeInstanceAsync in un ciclo o PurgeAllInstancesAsync con un filtro.
IDurableOrchestrationClient.RaiseEventAsync (sovraccarico del hub tra attività con taskHubName) Rimosso. Viene supportato solo l'evento di attivazione per lo stesso hub di attività.
IDurableEntityClient.SignalEntityAsync DurableTaskClient.Entities.SignalEntityAsync
IDurableEntityClient.SignalEntityAsync (sovraccarico dell'hub tra attività con taskHubName, connectionName) Rimosso. Sono supportate solo le operazioni di entità del "same-task-hub".
IDurableEntityClient.ReadEntityStateAsync DurableTaskClient.Entities.GetEntityAsync
IDurableEntityClient.ReadEntityStateAsync (sovraccarico dell'hub incarichi incrociati con taskHubName, connectionName) Rimosso. Sono supportate solo le operazioni di entità del "same-task-hub".
IDurableEntityClient.ListEntitiesAsync DurableTaskClient.Entities.GetAllEntitiesAsync
IDurableEntityClient.CleanEntityStorageAsync DurableTaskClient.Entities.CleanEntityStorageAsync (accetta CleanEntityStorageRequest l'oggetto anziché i parametri bool)
DurableOrchestrationStatus OrchestrationMetadata
DurableOrchestrationStatus.History Rimosso dall'oggetto di stato. Utilizzare invece DurableTaskClient.GetOrchestrationHistoryAsync.
PurgeHistoryResult PurgeResult
OrchestrationStatusQueryCondition OrchestrationQuery
OrchestrationStatusQueryResult AsyncPageable<OrchestrationMetadata>

API del contesto di orchestrazione

In-process (2.x) Lavoro isolato
IDurableOrchestrationContext TaskOrchestrationContext
IDurableOrchestrationContext.GetInput<T>() TaskOrchestrationContext.GetInput<T>() o inserire l'input come parametro: MyOrchestration([OrchestrationTrigger] TaskOrchestrationContext context, T input)
IDurableOrchestrationContext.SetOutput Rimosso. Usare il valore restituito dalla funzione orchestratore.
IDurableOrchestrationContext.CallActivityWithRetryAsync TaskOrchestrationContext.CallActivityAsync con un TaskOptions parametro per i dettagli di ripetizione dei tentativi.
IDurableOrchestrationContext.CallSubOrchestratorWithRetryAsync TaskOrchestrationContext.CallSubOrchestratorAsync con un TaskOptions parametro per i dettagli sui tentativi di ripetizione.
IDurableOrchestrationContext.CallHttpAsync TaskOrchestrationContext.CallHttpAsync
IDurableOrchestrationContext.CreateTimer<T>(DateTime, T, CancellationToken) TaskOrchestrationContext.CreateTimer(DateTime, CancellationToken). Parametro di stato rimosso.
IDurableOrchestrationContext.WaitForExternalEvent(string) (non generico) Rimosso. Utilizzare il WaitForExternalEvent<T>(string, CancellationToken).
IDurableOrchestrationContext.WaitForExternalEvent<T>(string, TimeSpan, T) (con defaultValue) Rimosso. Usare WaitForExternalEvent<T>(string, TimeSpan, CancellationToken), che genera TaskCanceledException in caso di timeout.
IDurableOrchestrationContext.ParentInstanceId TaskOrchestrationContext.Parent.InstanceId
IDurableOrchestrationContext.CreateReplaySafeLogger(ILogger) TaskOrchestrationContext.CreateReplaySafeLogger<T>() oppure TaskOrchestrationContext.CreateReplaySafeLogger(string)
IDurableOrchestrationContext.CreateEntityProxy<T> Rimosso. Usare Entities.CallEntityAsync o Entities.SignalEntityAsync direttamente.
IDurableOrchestrationContext.CallEntityAsync TaskOrchestrationContext.Entities.CallEntityAsync
IDurableOrchestrationContext.SignalEntity TaskOrchestrationContext.Entities.SignalEntityAsync
IDurableOrchestrationContext.LockAsync TaskOrchestrationContext.Entities.LockEntitiesAsync
IDurableOrchestrationContext.IsLocked TaskOrchestrationContext.Entities.InCriticalSection()
RetryOptions TaskOptions con TaskRetryOptions
DurableActivityContext Nessun equivalente
DurableActivityContext.GetInput<T>() Inserire l'input come parametro: MyActivity([ActivityTrigger] T input)
DurableHttpRequest (spazio dei nomi WebJobs) DurableHttpRequest (spazio dei nomi Microsoft.Azure.Functions.Worker.Extensions.DurableTask.Http)
DurableHttpResponse (spazio dei nomi WebJobs) DurableHttpResponse (spazio dei nomi Microsoft.Azure.Functions.Worker.Extensions.DurableTask.Http)

API di entità

In-process (2.x) Lavoro isolato
IDurableEntityContext TaskEntityContext
IDurableEntityContext.EntityName TaskEntityContext.Id.Name
IDurableEntityContext.EntityKey TaskEntityContext.Id.Key
IDurableEntityContext.OperationName TaskEntityOperation.Name
IDurableEntityContext.FunctionBindingContext Rimosso. Aggiungere FunctionContext come parametro di input.
IDurableEntityContext.HasState TaskEntityOperation.State.HasState
IDurableEntityContext.GetState TaskEntityOperation.State.GetState
IDurableEntityContext.SetState TaskEntityOperation.State.SetState
IDurableEntityContext.DeleteState TaskEntityOperation.State.SetState(null)
IDurableEntityContext.GetInput TaskEntityOperation.GetInput
IDurableEntityContext.Return Rimosso. Usare invece il valore restituito del metodo.
IDurableEntityContext.SignalEntity TaskEntityContext.SignalEntity. I segnali pianificati usano SignalEntityOptions.SignalTime anziché un sovraccarico di parametri DateTime.
IDurableEntityContext.StartNewOrchestration TaskEntityContext.ScheduleNewOrchestration. L'ID istanza viene impostato tramite StartOrchestrationOptions.InstanceId anziché un parametro stringa.
IDurableEntityContext.DispatchAsync TaskEntityDispatcher.DispatchAsync. Parametri del costruttore rimossi; usare invece l'inserimento delle dipendenze standard.
IDurableEntityContext.BatchSize Rimosso
IDurableEntityContext.BatchPosition Rimosso

Modifiche comportamentali

Avviso

ContinueAsNuova modifica predefinita: il preserveUnprocessedEvents parametro predefinito è stato modificato da false (2.x) a true (isolato). Se l'orchestrazione usa ContinueAsNew e si basa su eventi non elaborati scartati, passa esplicitamente preserveUnprocessedEvents: false.

Annotazioni

Cambio del valore predefinito di RestartAsync: il parametro predefinito è stato modificato da true (2.x) a false (isolato). Se il codice chiama RestartAsync e dipende da un nuovo ID istanza generato, passare restartWithNewInstanceId: true in modo esplicito.

  • Rimozione del proxy di entità: CreateEntityProxy<T> e gli overload del delegato tipizzato SignalEntityAsync<TEntityInterface>(Action<T>) non sono disponibili nel worker isolato. Chiamare Entities.CallEntityAsync o Entities.SignalEntityAsync direttamente con nomi di operazione basati su stringa anziché usare interfacce proxy tipate.
  • WaitForCompletionOrCreateCheckStatusResponseAsync: il timeout parametro è stato rimosso. Usare invece un CancellationToken con un timeout di cancellazione.
  • Operazioni tra hub attività rimosse: gli overload in-process che accettano taskHubName e connectionName (in RaiseEventAsync, SignalEntityAsync e ReadEntityStateAsync) non sono disponibili in contesto di lavoro isolato. Sono supportate solo le operazioni dello stesso task hub.
  • Operazioni batch per ID rimosse: gli overload e GetStatusAsync(IEnumerable<string>) in-process PurgeInstanceHistoryAsync(IEnumerable<string>) non sono disponibili nel ruolo di lavoro isolato. Usare GetAllInstancesAsync con un filtro OrchestrationQuery o chiama GetInstanceAsync/PurgeInstanceAsync singolarmente.
  • Cronologia orchestrazione spostata: DurableOrchestrationStatus.History (l'incorporato JArray) non fa più parte dell'oggetto stato. Usare l'API separata DurableTaskClient.GetOrchestrationHistoryAsync per recuperare la cronologia dell'orchestrazione.
  • Parametri del costruttore DispatchAsync rimossi: l'overload DispatchAsync<T>(params object[]) del parametro del costruttore non è disponibile. Le classi di entità vengono attivate tramite l'inserimento delle dipendenze standard. Registra le dipendenze dell'entità in Program.cs.
  • Modifiche al filtro delle query di entità: EntityQuery.EntityName viene sostituito da EntityQuery.InstanceIdStartsWithe EntityQuery.IncludeDeleted viene sostituito da EntityQuery.IncludeTransient.
  • Modifica della firma CleanEntityStorageAsync: invece di (bool removeEmptyEntities, bool releaseOrphanedLocks, CancellationToken), la versione isolata accetta un oggetto con le proprietà RemoveEmptyEntities e ReleaseOrphanedLocks.
  • Nuove API nel worker isolato: DurableTaskClient.GetOrchestrationHistoryAsync e il TaskOrchestrationContext.GetFunctionContext() metodo di estensione sono disponibili nel worker isolato, ma non hanno equivalenti in-process.

Aggiornare local.settings.json

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
        "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;Authentication=None"
    }
}

La modifica della chiave è FUNCTIONS_WORKER_RUNTIME da dotnet a dotnet-isolated.

Testare localmente

Avviare l'emulatore

docker run -d -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest

Eseguire l'applicazione di funzioni

func start

Verificare la funzionalità

Testare tutte le orchestrazioni, le attività e le entità per assicurarsi che funzionino correttamente:

  1. Avviare un'orchestrazione con un trigger HTTP
  2. Monitorare lo stato di orchestrazione
  3. Verificare l'ordine di esecuzione dell'attività
  4. Testare le operazioni di entità, se applicabile
  5. Controllare i dati di telemetria di Application Insights

Distribuire in Azure

Usare gli slot di distribuzione per minimizzare il downtime

  1. Creare uno slot di staging per l'app per le funzioni.
  2. Aggiornare la configurazione dello slot di staging:
    • Impostare FUNCTIONS_WORKER_RUNTIME su dotnet-isolated.
    • Aggiornare .NET versione dello stack, se necessario.
  3. Distribuire il codice migrato nello slot di staging.
  4. Eseguire un test approfondito nello slot di staging.
  5. Eseguire lo scambio di slot per spostare le modifiche nell'ambiente di produzione.

Aggiornare le impostazioni dell'applicazione

Nel portale di Azure o tramite l'interfaccia della riga di comando:

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

Aggiornare la configurazione dello stack

Se la destinazione è una versione .NET diversa:

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

Problemi comuni relativi alla migrazione

Problema: Errori di caricamento dell'assembly

Sintomo:Could not load file or assembly Errori.

Soluzione: Assicurarsi di rimuovere tutti i riferimenti al Microsoft.Azure.WebJobs.* pacchetto e sostituirli con equivalenti di lavoratori isolati.

Problema: Impossibile trovare l'attributo di associazione

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

Soluzione: Aggiungere il pacchetto di estensione appropriato e aggiornare le istruzioni di importazione:

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

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

Problema: IDurableOrchestrationContext non trovato

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

Soluzione: Sostituire con TaskOrchestrationContext:

using Microsoft.DurableTask;

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

Problema: differenze di serializzazione JSON

Sintomo: Errori di serializzazione o formati di dati imprevisti

Soluzione: Il modello isolato utilizza System.Text.Json per impostazione predefinita. Configurare la serializzazione in Program.cs:

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

Per usare invece Newtonsoft.Json:

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

Problema: Migrazione delle impostazioni di serializzazione personalizzate

Sintomo: È stato usato IMessageSerializerSettingsFactory nel modello in-process per personalizzare la serializzazione JSON per input di orchestrazione, output o stato dell'entità e hai bisogno dell'equivalente nel worker isolato.

Soluzione: L'interfaccia IMessageSerializerSettingsFactory non è disponibile nel worker isolato. Configurare invece il serializzatore a livello di lavoro in Program.cs:

Prima di (In-Process):

// Startup.cs
[assembly: FunctionsStartup(typeof(MyStartup))]
public class MyStartup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddSingleton<IMessageSerializerSettingsFactory, CustomSerializerSettingsFactory>();
    }
}

Dopo (Lavoratore Isolato)

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

Annotazioni

Questo approccio richiede i pacchetti NuGet Newtonsoft.Json e Azure.Core.Serialization. L'impostazione WorkerOptions.Serializer si applica a livello globale a tutti gli elementi serializzati dall'estensione Durable Functions. Per altri dettagli, vedere Serializzazione e persistenza in Durable Functions.

Checklist

Usare questo elenco di controllo per garantire una migrazione completa:

  • File di progetto aggiornato con <OutputType>Exe</OutputType>
  • Sostituito Microsoft.NET.Sdk.Functions con i pacchetti di lavoro
  • Sostituito Microsoft.Azure.WebJobs.Extensions.DurableTask con un pacchetto isolato
  • Creato Program.cs con la configurazione dell'host
  • Classe rimossa FunctionsStartup (se presente)
  • Aggiornati tutti [FunctionName] a [Function]
  • Sostituito IDurableOrchestrationContext con TaskOrchestrationContext
  • Sostituito IDurableOrchestrationClient con DurableTaskClient
  • Aggiornato il logging per utilizzare Dependency Injection o FunctionContext
  • Aggiornato il runtime di local.settings.json con dotnet-isolated
  • Tutte le istruzioni using Microsoft.Azure.WebJobs.* sono state rimosse
  • Aggiunte istruzioni using Microsoft.Azure.Functions.Worker
  • Sostituito CreateEntityProxy<T> con chiamate dirette CallEntityAsync/SignalEntityAsync
  • Sostituzione degli overload delle operazioni tra più attività (se usato)
  • Sostituite le chiamate batch GetStatusAsync/PurgeInstanceHistoryAsync by-ID con chiamate singole o basate su filtro
  • Accesso migrato DurableOrchestrationStatus.History a GetOrchestrationHistoryAsync
  • Parametri del costruttore di entità DispatchAsync aggiornati per l'uso dell'inserimento delle dipendenze
  • Tutte le funzioni sono state testate in locale
  • Distribuito nello slot di staging e verificato
  • Scambiato in produzione

Passaggi successivi

Risorse aggiuntive