Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Esta guía le guía a través de la migración de la aplicación de .NET Durable Functions desde el modelo en proceso al modelo de trabajo aislado. El modelo en proceso llega al final del soporte técnico el 10 de noviembre de 2026. Después de esa fecha, no se proporcionan actualizaciones de seguridad ni correcciones de errores. El modelo de trabajador aislado también proporciona control total del proceso, inyección de dependencias estándar de .NET y acceso a las funcionalidades más recientes de la plataforma.
Advertencia
La compatibilidad con el modelo en proceso finaliza el 10 de noviembre de 2026. Se recomienda migrar ahora. Para obtener información general sobre el modelo de trabajo aislado, consulte .NET información general sobre el proceso de trabajo aislado.
Prerrequisitos
- Azure Functions Core Tools v4.x o posterior
- SDK de .NET 8.0 (o la versión de .NET de destino)
- Visual Studio 2022 o VS Code with Azure Functions extension
Información general sobre la migración
En un nivel alto, la migración requiere estos pasos:
- Actualizar el archivo del proyecto : cambie a la salida ejecutable, reemplace los paquetes.
- Agregar Program.cs : cree el punto de entrada del host.
- Actualizar referencias de paquete : intercambio de paquetes en proceso para equivalentes aislados
- Actualizar código de función : cambiar atributos, tipos y espacios de nombres
-
Actualizar local.settings.json : establezca el entorno de ejecución en
dotnet-isolated - Probar localmente y desplegar en Azure
Lista de comprobación de referencia rápida
Use esta lista de comprobación para realizar un seguimiento del progreso. Una versión detallada está al final de esta guía.
- [ ] Archivo del proyecto: se agregó
<OutputType>Exe</OutputType>, paquetes reemplazados - [ ] Creado
Program.cs, eliminadoFunctionsStartup - [ ] Se ha actualizado
[FunctionName]→[Function], se han reemplazado los tipos de contexto o cliente. - [ ] Se han quitado todas las referencias de
Microsoft.Azure.WebJobs.* - [ ] Actualizado
local.settings.jsonadotnet-isolated - [ ] Probado localmente e implementado
Identificación de aplicaciones para migrar (opcional)
Si no está seguro de qué aplicaciones siguen usando el modelo en proceso, ejecute este script Azure PowerShell:
$FunctionApps = Get-AzFunctionApp
$AppInfo = @{}
foreach ($App in $FunctionApps)
{
if ($App.Runtime -eq 'dotnet')
{
$AppInfo.Add($App.Name, $App.Runtime)
}
}
$AppInfo
Las aplicaciones que muestran dotnet como tiempo de ejecución usan el modelo de proceso. Las aplicaciones que ya muestran dotnet-isolated usan el modelo de trabajo aislado.
Actualización del archivo del proyecto
Antes (en proceso)
<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>
Después (trabajador aislado)
<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>
Los cambios principales son cambiar a un tipo de salida ejecutable y reemplazar todos los paquetes Microsoft.Azure.WebJobs.* por sus equivalentes Microsoft.Azure.Functions.Worker.*.
Agregar Program.cs
El modelo de trabajador aislado requiere un Program.cs punto de entrada. Cree este archivo en la raíz del proyecto. Si tiene una FunctionsStartup clase en Startup.cs, mueva esos registros de servicio al ConfigureServices bloque y elimine 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();
Actualización de las referencias del paquete
mapeo de paquetes de Durable Functions
| Paquete en proceso | Paquete de trabajador aislado |
|---|---|
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 |
Mapeo de paquetes de extensión comunes
| En proceso | Trabajador aislado |
|---|---|
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
Elimine las referencias a espacios de nombres Microsoft.Azure.WebJobs.* y Microsoft.Azure.Functions.Extensions del proyecto.
Actualizar código de función
En esta sección se tratan los cambios de código para cada tipo de Durable Functions. Vaya a la sección de los tipos de función que usa la aplicación:
- Cambios en el espacio de nombres
- Funciones de Orquestador
- Funciones de actividad
- Funciones de cliente
- Directivas de reintento (si se usan)
- Funciones de entidad (si se usan)
Para obtener una descripción detallada de cada API, consulte la referencia de API.
Cambios en el espacio de nombres
// 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;
Cambios en el atributo de función
// Before (In-Process)
[FunctionName("MyOrchestrator")]
// After (Isolated Worker)
[Function(nameof(MyOrchestrator))]
Cambios de función de Orchestrator
Antes (En proceso):
[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 };
}
Después (trabajo aislado):
[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 };
}
Diferencias clave
| Aspecto | En proceso | Trabajador aislado |
|---|---|---|
| Tipo de contexto | IDurableOrchestrationContext |
TaskOrchestrationContext |
| Logger | Parámetro ILogger |
context.CreateReplaySafeLogger() |
| Atributo | [FunctionName] |
[Function] |
Cambios en la función de actividad
Antes (En proceso):
[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;
}
Después (trabajo aislado):
[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;
}
Cambios en la función de cliente
Antes (En proceso):
[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);
}
Después (trabajo aislado):
[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);
}
Cambios en el tipo de cliente
| En proceso | Trabajador aislado |
|---|---|
IDurableOrchestrationClient |
DurableTaskClient |
StartNewAsync() |
ScheduleNewOrchestrationInstanceAsync() |
CreateCheckStatusResponse() |
CreateCheckStatusResponseAsync() |
HttpRequest / IActionResult |
HttpRequestData / HttpResponseData |
Cambios en la política de reintento
En proceso usa RetryOptions con CallActivityWithRetryAsync. El trabajador aislado utiliza TaskOptions con el estándar CallActivityAsync.
Antes (En proceso):
var retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: 3);
string result = await context.CallActivityWithRetryAsync<string>(
"MyActivity", retryOptions, input);
Después (trabajo aislado):
var retryOptions = new TaskOptions(
new TaskRetryOptions(new RetryPolicy(
maxNumberOfAttempts: 3,
firstRetryInterval: TimeSpan.FromSeconds(5))));
string result = await context.CallActivityAsync<string>(
"MyActivity", input, retryOptions);
Cambios en la función de entidad
Antes (En proceso):
[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;
}
}
Después (trabajo aislado):
[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;
}
Cambios de comportamiento disruptivos
Revise estos cambios antes de probar la aplicación migrada. Para ver el mapeo completo de cada API, consulte la documentación de API.
Advertencia
Se ha cambiado el valor predeterminado de serialización: el trabajo aislado usa System.Text.Json de forma predeterminada en lugar de Newtonsoft.Json. Si las orquestaciones pasan objetos complejos, realice pruebas cuidadosas de la serialización. Consulte Diferencias de serialización de JSON para conocer las opciones de configuración.
Advertencia
ContinueAsNuevo cambio predeterminado: el preserveUnprocessedEvents parámetro predeterminado cambió de false (2.x) a true (aislado). Si la orquestación utiliza ContinueAsNew y depende de que se descarten los eventos no procesados, pase preserveUnprocessedEvents: false explícitamente.
Nota:
Cambio predeterminado restartAsync: el parámetro restartWithNewInstanceId, cuyo valor predeterminado era true (2.x), ha cambiado a false (aislado). Si el código llama a RestartAsync y depende de que se genere un nuevo identificador de instancia, pase restartWithNewInstanceId: true explícitamente.
Otros cambios importantes:
-
Proxies de entidad eliminados —
CreateEntityProxy<T>no está disponible. UseEntities.CallEntityAsyncoEntities.SignalEntityAsyncdirectamente. -
Operaciones entre centros de tareas quitadas
taskHubName/connectionName: las sobrecargas aceptadas no están disponibles. Solo se admiten las operaciones del mismo centro de tareas. -
Historial de orquestaciones movido :
DurableOrchestrationStatus.Historyya no está en el objeto de estado. UtiliceDurableTaskClient.GetOrchestrationHistoryAsync.
Actualizar local.settings.json
El cambio de clave es establecer FUNCTIONS_WORKER_RUNTIME de dotnet a dotnet-isolated:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
Nota:
La migración no modifica la configuración del back-end de almacenamiento (Azure Storage, MSSQL, Netherite o Durable Task Scheduler). Mantenga la configuración relacionada con el almacenamiento existente.
Probar localmente
Ejecute la aplicación de funciones localmente y compruebe que todas las orquestaciones, actividades y entidades funcionan correctamente.
func start
Comprobación de la funcionalidad
Pruebe los escenarios siguientes según corresponda:
- Inicio de una orquestación con un desencadenador HTTP
- Supervisión del estado de la orquestación
- Comprobación del orden de ejecución de la actividad
- Prueba de operaciones de entidad si procede
- Comprobación de la telemetría de Application Insights
Implementar en Azure
Recomendado: use intervalos de implementación
Utiliza espacios de implementación para minimizar el tiempo de inactividad:
- Cree un entorno de ensayo para su aplicación de funciones.
-
Actualización de la configuración del slot de puesta en escena:
- Establece
FUNCTIONS_WORKER_RUNTIMEendotnet-isolated. - Actualice la versión del stack de .NET si es necesario.
- Establece
- Implemente el código migrado en la ranura de ensayo.
- Pruebe exhaustivamente en el intervalo de ensayo.
- Realice el intercambio de intervalos para implantar cambios en producción.
Actualización de la configuración de la aplicación
En el portal de Azure o a través de la CLI:
az functionapp config appsettings set \
--name <FUNCTION_APP_NAME> \
--resource-group <RESOURCE_GROUP> \
--settings FUNCTIONS_WORKER_RUNTIME=dotnet-isolated
Actualización de la configuración de la pila
Si tiene como destino una versión de .NET diferente:
az functionapp config set \
--name <FUNCTION_APP_NAME> \
--resource-group <RESOURCE_GROUP> \
--net-framework-version v8.0
Problemas comunes de migración
Problema: Errores de carga de ensamblados
Síntoma:Could not load file or assembly errores.
Solución: Asegúrese de quitar todas las referencias de paquete Microsoft.Azure.WebJobs.* y reemplazarlas por equivalentes de trabajadores aislados.
Problema: No se encontró el atributo de enlace
Síntoma:The type or namespace 'QueueTrigger' could not be found
Solución: agregue el paquete de extensión adecuado y actualice con las instrucciones:
// Add using statement
using Microsoft.Azure.Functions.Worker;
// Install package
// dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues
Problema: No se encontró IDurableOrchestrationContext
Síntoma:The type or namespace 'IDurableOrchestrationContext' could not be found
Solución: Reemplace por TaskOrchestrationContext:
using Microsoft.DurableTask;
[Function(nameof(MyOrchestrator))]
public static async Task MyOrchestrator([OrchestrationTrigger] TaskOrchestrationContext context)
{
// ...
}
Problema: diferencias de serialización JSON
Síntoma: Errores de serialización o formatos de datos inesperados
Solución: El modelo aislado usa System.Text.Json de forma predeterminada. Configure la serialización en Program.cs:
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureServices(services => {
services.Configure<JsonSerializerOptions>(options => {
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
})
.Build();
Para usar Newtonsoft.Json en su lugar:
services.Configure<WorkerOptions>(options => {
options.Serializer = new NewtonsoftJsonObjectSerializer();
});
Problema: Migración de la configuración de serialización personalizada
Síntoma: Ha usado IMessageSerializerSettingsFactory en el modelo en-proceso y necesita el equivalente en el trabajador aislado.
Solución: Configure el serializador a nivel de trabajador en Program.cs. Para obtener más información, consulte la sección cambios de comportamiento de la referencia de la API y Serialización y persistencia en Funciones Duraderas.
Para usar Newtonsoft.Json con la configuración personalizada:
// 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();
Nota:
Este enfoque requiere los paquetes NuGet Newtonsoft.Json y Azure.Core.Serialization.
Lista de comprobación
Use esta lista de comprobación para garantizar una migración completa:
- Archivo de proyecto actualizado con
<OutputType>Exe</OutputType> - Se ha reemplazado
Microsoft.NET.Sdk.Functionspor paquetes de trabajo - Se ha reemplazado
Microsoft.Azure.WebJobs.Extensions.DurableTaskpor un paquete aislado - Creado
Program.cscon la configuración del host - Clase eliminada
FunctionsStartup(si está presente) - Se ha actualizado todo
[FunctionName]a[Function] - Reemplazado por
IDurableOrchestrationContextTaskOrchestrationContext - Reemplazado por
IDurableOrchestrationClientDurableTaskClient - Registro actualizado para usar DI o
FunctionContext - Se ha actualizado
local.settings.jsoncon el entorno de ejecucióndotnet-isolated - Se han quitado todo
Microsoft.Azure.WebJobs.*con instrucciones - Se han agregado
Microsoft.Azure.Functions.Workerinstrucciones using - Se ha reemplazado
CreateEntityProxy<T>por las llamadas directas aCallEntityAsync/SignalEntityAsync - Sobrecargas de operación de centros de tareas cruzados reemplazadas (si se utilizan)
- Se han reemplazado las llamadas por ID
GetStatusAsync/PurgeInstanceHistoryAsyncpor lotes por llamadas individuales o basadas en filtros - Acceso migrado
DurableOrchestrationStatus.HistoryaGetOrchestrationHistoryAsync - Se han actualizado los parámetros del constructor
DispatchAsyncde la entidad para usar DI - Prueba local de todas las funciones
- Se ha implementado en el intervalo de ensayo y comprobado
- Se ha cambiado a producción
Pasos siguientes
- Mapeo de API de trabajador aislado a en proceso: referencia completa de API para su migración
- Información general sobre .NET Durable Functions para trabajador aislado
- Durable Functions versiones y guía de migración
Contenido relacionado
- Guía oficial de migración de Microsoft (todas las Funciones de Azure)
- Información general del proceso de trabajo aislado de .NET
- Diferencias del modelo de trabajador aislado
- Serialización y persistencia en Durable Functions
- Despliegue sin tiempo de inactividad para Durable Functions
- Configurar el programador de tareas duraderas