你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
本指南将指导你将.NET Durable Functions应用从进程内模型迁移到独立的辅助角色模型。 进程内模型于 2026 年 11 月 10 日终止支持。 在该日期之后,不会提供安全更新或 bug 修复。 独立工作者模型还提供了完整的进程控制、标准.NET依赖注入功能,并可以访问最新的平台功能。
警告
对进程内模型的支持将于 2026 年 11 月 10 日结束。 建议立即迁移。 有关隔离工作者模型的背景信息,请参阅.NET隔离工作进程概述。
迁移清单
使用以下清单跟踪每个迁移步骤的进度:
| Step | Section |
|---|---|
| 1.验证先决条件 | Prerequisites |
| 2.更新项目文件 | 更新项目文件 |
| 3.添加Program.cs | 添加Program.cs |
| 4. 更新包引用 | 更新包引用 |
| 5. 更新函数代码 | 更新函数代码 |
| 6. 更新 local.settings.json | 更新 local.settings.json |
| 7. 在本地测试 | 在本地测试 |
| 8. 部署到 Azure | 部署到 Azure |
先决条件
- Azure Functions Core Tools v4.x 或更高版本
- .NET 8.0 SDK(或目标.NET版本)
- Visual Studio 2022 或具有 Azure Functions 扩展名的 VS 代码
确定要迁移的应用(可选)
如果不确定哪些应用仍在使用进程内模型,请运行以下Azure PowerShell脚本:
$FunctionApps = Get-AzFunctionApp
$AppInfo = @{}
foreach ($App in $FunctionApps)
{
if ($App.Runtime -eq 'dotnet')
{
$AppInfo.Add($App.Name, $App.Runtime)
}
}
$AppInfo
显示dotnet作为运行时的应用程序使用进程内模型。 显示 dotnet-isolated 已使用独立工作模型的应用。
更新项目文件
之前(处理中)
<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>
之后(独立辅助角色)
<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>
主要更改是切换到可执行输出类型,并将所有 Microsoft.Azure.WebJobs.* 包替换为其Microsoft.Azure.Functions.Worker.*等效项。
添加Program.cs
独立工作者模型需要一个入口点 Program.cs。 在项目根目录中创建此文件。 如果你在 FunctionsStartup 中有一个 Startup.cs 类,请将这些服务注册移到 ConfigureServices 块中并删除 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();
更新包引用
Durable Functions 包映射
| 进程中的软件包 | 隔离工作器包 |
|---|---|
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 |
常见扩展包映射
| 进行中 | 独立工人 |
|---|---|
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 |
重要
从项目中删除对 Microsoft.Azure.WebJobs.* 命名空间和 Microsoft.Azure.Functions.Extensions 的任何引用。
更新函数代码
本部分介绍每个Durable Functions类型的代码更改。 跳转到应用使用的函数类型的部分:
有关每个 API 的完整映射,请参阅 API 参考。
命名空间更改
// 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;
函数属性更改
// Before (In-Process)
[FunctionName("MyOrchestrator")]
// After (Isolated Worker)
[Function(nameof(MyOrchestrator))]
编排器功能更改
之前(处理中):
[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 };
}
之后(隔离工作者):
[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 };
}
主要差异
| 方面 | 进行中 | 单独作业人员 |
|---|---|---|
| 上下文类型 | IDurableOrchestrationContext |
TaskOrchestrationContext |
| 记录器 |
ILogger 参数 |
context.CreateReplaySafeLogger() |
| Attribute | [FunctionName] |
[Function] |
活动函数更改
之前(处理中):
[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;
}
之后(隔离工作者):
[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;
}
客户端函数更改
之前(处理中):
[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);
}
之后(隔离工作者):
[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);
}
客户端类型更改
| 进行中 | 独立工人 |
|---|---|
IDurableOrchestrationClient |
DurableTaskClient |
StartNewAsync() |
ScheduleNewOrchestrationInstanceAsync() |
CreateCheckStatusResponse() |
CreateCheckStatusResponseAsync() |
HttpRequest / IActionResult |
HttpRequestData / HttpResponseData |
重试策略更改
进程内用法 RetryOptions 与 CallActivityWithRetryAsync. 隔离工作器使用 TaskOptions 配合标准 CallActivityAsync。
之前(处理中):
var retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: 3);
string result = await context.CallActivityWithRetryAsync<string>(
"MyActivity", retryOptions, input);
之后(隔离工作者):
var retryOptions = new TaskOptions(
new TaskRetryOptions(new RetryPolicy(
maxNumberOfAttempts: 3,
firstRetryInterval: TimeSpan.FromSeconds(5))));
string result = await context.CallActivityAsync<string>(
"MyActivity", input, retryOptions);
实体函数更改
之前(处理中):
[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;
}
}
之后(隔离工作者):
[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;
}
中断性行为变更
在测试迁移的应用之前,请查看这些更改。 有关每个 API 的完整映射,请参阅 API 参考。
警告
序列化默认值已更改:独立辅助角色默认使用 System.Text.Json ,而不是 Newtonsoft.Json。 如果你的业务流程传递复杂对象,请仔细测试序列化。 有关配置选项 ,请参阅 JSON 序列化差异 。
警告
ContinueAsNew 默认更改: preserveUnprocessedEvents 参数默认值从 false (2.x) 更改为 true (独立)。 如果编排使用 ContinueAsNew 且依赖于丢弃未处理事件,请显式传递 preserveUnprocessedEvents: false。
注释
RestartAsync 默认更改: restartWithNewInstanceId 参数默认值从 true (2.x) 更改为 false (独立)。 如果代码调用 RestartAsync 并依赖于正在生成的新实例 ID,请显式传递 restartWithNewInstanceId: true。
其他值得注意的更改:
-
删除实体代理 -
CreateEntityProxy<T>不可用。 请直接使用Entities.CallEntityAsync或Entities.SignalEntityAsync。 -
已删除跨任务中心操作 - 接受
taskHubName/connectionName的重载不可用。 仅支持相同任务中心的操作。 -
业务流程历史记录已移动 -
DurableOrchestrationStatus.History不再位于状态对象中。 使用DurableTaskClient.GetOrchestrationHistoryAsync。
更新 local.settings.json
主要更改是将FUNCTIONS_WORKER_RUNTIME从dotnet设置为dotnet-isolated。
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
注释
迁移不会更改存储后端配置(Azure 存储、MSSQL、Netherite 或 Durable Task Scheduler)。 保留与存储相关的现有设置。
在本地测试
在本地运行函数应用并验证所有业务流程、活动和实体是否正常工作。
func start
验证功能
根据情况测试以下方案:
- 使用 HTTP 触发器启动编排
- 监视编排状态
- 验证活动执行顺序
- 测试实体操作(如果适用的情况下)
- 检查 Application Insights 的遥测数据
部署到 Azure 云
建议:使用部署插槽
使用部署槽位将停机时间降到最低:
- 为函数应用创建过渡槽。
-
更新过渡槽配置:
- 将
FUNCTIONS_WORKER_RUNTIME设置为dotnet-isolated。 - 根据需要更新.NET堆栈版本。
- 将
- 将迁移的代码 部署到过渡槽。
- 在预发布槽中进行充分测试。
- 执行槽交换以将更改移动到生产环境。
更新应用程序设置
在 Azure 门户中或者通过 CLI:
az functionapp config appsettings set \
--name <FUNCTION_APP_NAME> \
--resource-group <RESOURCE_GROUP> \
--settings FUNCTIONS_WORKER_RUNTIME=dotnet-isolated
更新堆栈配置
如果面向其他 .NET 版本,请按照以下步骤进行操作:
az functionapp config set \
--name <FUNCTION_APP_NAME> \
--resource-group <RESOURCE_GROUP> \
--net-framework-version v8.0
常见迁移问题
问题:程序集加载错误
症状:Could not load file or assembly 错误。
解决方案:确保删除所有 Microsoft.Azure.WebJobs.* 包引用,并将其替换为隔离工作器等效项。
问题:找不到绑定属性
症状:The type or namespace 'QueueTrigger' could not be found
解决方案:添加相应的扩展包并更新使用语句:
// Add using statement
using Microsoft.Azure.Functions.Worker;
// Install package
// dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues
问题:找不到 IDurableOrchestrationContext
症状:The type or namespace 'IDurableOrchestrationContext' could not be found
解决 方案: 替换为 TaskOrchestrationContext:
using Microsoft.DurableTask;
[Function(nameof(MyOrchestrator))]
public static async Task MyOrchestrator([OrchestrationTrigger] TaskOrchestrationContext context)
{
// ...
}
问题:JSON 序列化差异
症状: 序列化错误或意外的数据格式
解决 方案: 独立模型默认使用 System.Text.Json 。 在Program.cs中配置序列化
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureServices(services => {
services.Configure<JsonSerializerOptions>(options => {
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
})
.Build();
改用 Newtonsoft.Json:
services.Configure<WorkerOptions>(options => {
options.Serializer = new NewtonsoftJsonObjectSerializer();
});
问题:迁移自定义序列化设置
症状:你在进程内模型中使用了 IMessageSerializerSettingsFactory,并需要在独立辅助角色中找到等效方案。
解决方案: 在Program.cs中配置工作者级别序列化程序。 有关详细信息,请参阅 API 参考中的行为更改部分,以及Durable Functions 中的序列化和持久性。
若要将 Newtonsoft.Json 与自定义设置配合使用,请执行以下操作:
// 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();
注释
此方法需要 Newtonsoft.Json 和 Azure.Core.Serialization NuGet 包。
清单
使用此清单来确保完成迁移:
- 更新了包含
<OutputType>Exe</OutputType>的项目文件 - 将
Microsoft.NET.Sdk.Functions替换为工作器包 - 已将
Microsoft.Azure.WebJobs.Extensions.DurableTask替换为独立包 - 使用主机配置创建
Program.cs - 已删除
FunctionsStartup类(如果存在) - 已全部
[FunctionName]更新为[Function] - 将
IDurableOrchestrationContext替换为TaskOrchestrationContext - 将
IDurableOrchestrationClient替换为DurableTaskClient - 更新了日志记录以使用 DI 或
FunctionContext - 已使用
local.settings.json运行时更新dotnet-isolated - 移除所有
Microsoft.Azure.WebJobs.*使用语句 - 添加了
Microsoft.Azure.Functions.Workerusing 语句 - 将
CreateEntityProxy<T>替换为直接CallEntityAsync/SignalEntityAsync调用 - 替换跨任务中心操作重载(如果使用)
- 将按 ID 批处理的
GetStatusAsync/PurgeInstanceHistoryAsync调用替换为基于筛选器或单独调用 -
DurableOrchestrationStatus.History的访问已迁移到GetOrchestrationHistoryAsync - 更新了实体
DispatchAsync构造函数参数以使用 DI - 在本地测试所有函数
- 部署到预发布槽并验证
- 交换到生产环境
后续步骤
- 进程内到独立辅助角色 API 映射 - 专为你的迁移提供的完整 API 参考文档
- .NET 隔离工作器的 Durable Functions 概述
- Durable Functions版本和迁移指南