Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questa guida illustra come eseguire la migrazione dell'app .NET Durable Functions dal modello in-process al modello di lavoro isolato. Il modello in-process raggiunge la fine del supporto il 10 novembre 2026. Dopo tale data, non vengono forniti aggiornamenti della sicurezza o correzioni di bug. Il modello di lavoro isolato offre anche il controllo completo dei processi, l'inserimento di dipendenze standard .NET e l'accesso alle funzionalità della piattaforma più recenti.
Avvertimento
Il supporto per il modello in-process termina il 10 novembre 2026. È consigliabile eseguire la migrazione ora. Per informazioni generali sul modello di lavoro isolato, vedere .NET panoramica del processo di lavoro isolato.
Elenco di controllo per la migrazione
Usare l'elenco di controllo seguente per tenere traccia dello stato di avanzamento in ogni passaggio della migrazione:
| Passo | Sezione |
|---|---|
| 1. Verificare i prerequisiti | Prerequisiti |
| 2. Aggiornare il file di progetto | Aggiornare il file di progetto |
| 3. Aggiungere Program.cs | Aggiungere Program.cs |
| 4. Aggiornare i riferimenti ai pacchetti | Aggiornare i riferimenti ai pacchetti |
| 5. Aggiornare il codice della funzione | Aggiornare il codice della funzione |
| 6. Aggiornare local.settings.json | Aggiornare local.settings.json |
| 7. Testare localmente | Testare localmente |
| 8. Eseguire la distribuzione in Azure | Distribuzione in Azure |
Prerequisiti
- Funzioni di Azure Core Tools v4.x o versione successiva
- .NET 8.0 SDK (o versione di .NET di destinazione)
- Visual Studio 2022 o VS Code with Funzioni di Azure extension
Identificare le app di cui eseguire la migrazione (facoltativo)
Se non si è certi delle app che usano ancora il modello in-process, eseguire questo script Azure PowerShell:
$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 mostrano dotnet-isolated già usano il modello di lavoro isolato.
Aggiornare il file di progetto
Prima (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>
Dopo (worker isolato)
<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>
Le modifiche principali sono il passaggio a un tipo di output eseguibile e la sostituzione di tutti i pacchetti Microsoft.Azure.WebJobs.* con i relativi equivalenti Microsoft.Azure.Functions.Worker.*.
Aggiungere Program.cs
Il modello di lavoratore isolato richiede un punto di ingresso Program.cs. Creare questo file nella radice del progetto. Se si dispone una FunctionsStartup classe in Startup.cs, spostare le registrazioni di servizio nel ConfigureServices blocco ed eliminare 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();
Aggiornare i riferimenti ai pacchetti
Mapping dei pacchetti 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 |
Mappatura dei pacchetti di estensione comuni
| 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
Questa sezione illustra le modifiche al codice per ogni tipo di Durable Functions. Passare alla sezione per i tipi di funzione usati dall'app:
- Modifiche dello spazio dei nomi
- Funzioni dell'agente di orchestrazione
- Funzioni di attività
- Funzioni client
- Criteri di ripetizione dei tentativi (se usati)
- Funzioni di entità (se usate)
Per un mapping completo dell'API per API, vedere le informazioni di riferimento sulle API.
Modifiche allo 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 (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 (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;
}
Dopo (Lavoratore isolato):
[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 (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 (Lavoratore isolato):
[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 (in-process):
var retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: 3);
string result = await context.CallActivityWithRetryAsync<string>(
"MyActivity", retryOptions, input);
Dopo (Lavoratore isolato):
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 (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 (Lavoratore isolato):
[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;
}
Modifiche importanti del comportamento
Esaminare queste modifiche prima di testare l'app migrata. Per il mapping completo dell'API per API, vedere le informazioni di riferimento sull'API.
Avvertimento
Serializzazione predefinita modificata: il lavoratore isolato usa System.Text.Json per impostazione predefinita anziché Newtonsoft.Json. Se le tue orchestrazioni superano oggetti complessi, testare attentamente la serializzazione. Vedere Differenze di serializzazione JSON per le opzioni di configurazione.
Avvertimento
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 rimossi, passare preserveUnprocessedEvents: false in modo esplicito.
Annotazioni
Cambio del valore predefinito di RestartAsync: il parametro predefinito è stato modificato da restartWithNewInstanceId (2.x) a true (isolato). Se il codice chiama RestartAsync e dipende da un nuovo ID istanza generato, passare restartWithNewInstanceId: true in modo esplicito.
Altre modifiche rilevanti:
-
Proxy di entità rimossi —
CreateEntityProxy<T>non è disponibile. UsareEntities.CallEntityAsyncoEntities.SignalEntityAsyncdirettamente. -
Operazioni cross-task-hub rimosse — gli overload che accettavano
taskHubName/connectionNamenon sono disponibili. Sono supportate solo le operazioni dello stesso task hub. -
Cronologia orchestrazione spostata —
DurableOrchestrationStatus.Historynon è più presente nell'oggetto di stato. Utilizzare ilDurableTaskClient.GetOrchestrationHistoryAsync.
Aggiornare local.settings.json
La modifica della chiave viene impostata FUNCTIONS_WORKER_RUNTIME da dotnet a dotnet-isolated:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
Annotazioni
La configurazione back-end di archiviazione (Archiviazione di Azure, MSSQL, Netherite o Durable Task Scheduler) rimane invariata dalla migrazione. Mantenere le impostazioni correlate all'archiviazione esistenti.
Testare localmente
Esegui l'applicazione delle funzioni localmente e verifica che tutte le orchestrazioni, le attività e le entità funzionino correttamente.
func start
Verificare la funzionalità
Testare gli scenari seguenti in base alle esigenze:
- Avviare un'orchestrazione con un trigger HTTP
- Monitorare lo stato di orchestrazione
- Verificare l'ordine di esecuzione dell'attività
- Testare le operazioni di entità, se applicabile
- Controllare i dati di telemetria di Application Insights
Distribuzione su Azure
Consigliato: usare gli slot di distribuzione
Usare gli slot di distribuzione per minimizzare il downtime
- Creare uno slot di staging per l'app per le funzioni.
-
Aggiornare la configurazione dello slot di staging:
- Impostare
FUNCTIONS_WORKER_RUNTIMEsudotnet-isolated. - Aggiornare .NET versione dello stack, se necessario.
- Impostare
- Distribuire il codice migrato nello slot di staging.
- Eseguire un test approfondito nello slot di staging.
- 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: rimuovere tutti i Microsoft.Azure.WebJobs.* riferimenti al pacchetto e sostituirli con processi di lavoro isolati equivalenti.
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 using:
// 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: hai usato IMessageSerializerSettingsFactory nel modello in-process ed è necessario l'equivalente nel processo di lavoro isolato.
Soluzione: configurare il serializzatore a livello di worker in Program.cs. Per informazioni dettagliate, vedere la sezione delle modifiche comportamentali della documentazione API e Serializzazione e persistenza in Durable Functions.
Per usare Newtonsoft.Json con impostazioni personalizzate:
// 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.
Checklist
Usare questo elenco di controllo per garantire una migrazione completa:
- File di progetto aggiornato con
<OutputType>Exe</OutputType> - Sostituito
Microsoft.NET.Sdk.Functionscon i pacchetti di lavoro - Sostituito
Microsoft.Azure.WebJobs.Extensions.DurableTaskcon un pacchetto isolato - Creato
Program.cscon la configurazione dell'host - Classe rimossa
FunctionsStartup(se presente) - Aggiornati tutti
[FunctionName]a[Function] - Sostituito
IDurableOrchestrationContextconTaskOrchestrationContext - Sostituito
IDurableOrchestrationClientconDurableTaskClient - Registrazione aggiornata per l'uso di DI o
FunctionContext - Aggiornato il runtime di
local.settings.jsoncondotnet-isolated - Sono state rimosse tutte le istruzioni using di
Microsoft.Azure.WebJobs.* - Sono state aggiunte istruzioni using di
Microsoft.Azure.Functions.Worker - Sostituito
CreateEntityProxy<T>con chiamate diretteCallEntityAsync/SignalEntityAsync - Sostituzione degli overload delle operazioni tra hub di attività (se in uso)
- Sostituite le chiamate batch
GetStatusAsync/PurgeInstanceHistoryAsyncper ID con chiamate singole o basate su filtro - Accesso migrato
DurableOrchestrationStatus.HistoryaGetOrchestrationHistoryAsync - Parametri del costruttore
DispatchAsyncdi entità 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
- Mappatura dell'API da in-process a worker isolato: riferimento completo dell'API per la tua migrazione
- Panoramica di Durable Functions per il processo di lavoro .NET isolato
- Versioni di Durable Functions e guida alla migrazione
Contenuti correlati
- Guida alla migrazione di Microsoft ufficiale (tutti i Funzioni di Azure)
- Panoramica del processo di lavoro isolato di .NET
- Differenze del modello di lavoro isolato
- Serializzazione e persistenza in Durable Functions
- Distribuzione senza tempi di inattività per Durable Functions
- Configurare il Schedulatore di Attività Durevoli