将 .NET 应用从进程内模型迁移到独立辅助角色模型

重要

对进程内模型的支持将于 2026 年 11 月 10 日结束。 强烈建议按照本文中的说明将应用迁移到独立的辅助角色模型。

本文将引导你完成将 .NET 函数应用程序从进程内模型安全迁移到独立辅助角色模型的过程。 若要了解这些模型之间的大致差异,请参阅执行模式比较

本指南假设你的应用程序在 Functions 运行时版本 4.x 上运行。 否则,应遵循升级主机版本的指南:

浏览这些主机版本迁移指南还可以帮助你迁移到独立工作器模型。

确定要迁移的函数应用

使用以下 Azure PowerShell 脚本,在订阅中生成当前使用进程内模型的函数应用的列表。

该脚本使用 Azure PowerShell 当前配置为使用的订阅。 可以更改订阅,方法是先运行 Set-AzContext -Subscription '<YOUR SUBSCRIPTION ID>' 并将 <YOUR SUBSCRIPTION ID> 替换为要评估的订阅的 ID。

$FunctionApps = Get-AzFunctionApp

$AppInfo = @{}

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

$AppInfo

选择目标 .NET 版本

在 Functions 运行时版本 4.x 上,.NET 函数应用在使用进程内模型时以 .NET 6 为目标。

将函数应用迁移到版本 4.x 时,可以选择 .NET 的目标版本。 可以将 C# 项目升级到以下 .NET 版本之一,所有这些版本都可以在 Functions 版本 4.x 上运行:

.NET 版本 .NET 官方支持策略版本类型 函数进程模型1
.NET 7 STS(2024 年 5 月 14 日结束支持) 独立工作模型
.NET 6 LTS(2024 年 11 月 12 日结束支持) 独立工作模型
进程模型
.NET Framework 4.8 查看策略 独立工作模型

1独立工作模型支持 .NET 的长期支持 (LTS) 和标准期限支持 (STS),以及 .NET Framework。 而进程内模型仅支持 .NET 的 LTS 版本。 有关两类模型之间完整的特性和功能比较,请参阅进程内和独立工作进程 .NET Azure Functions 之间的差异

提示

我们建议在独立辅助角色模型上升级到 .NET 8。 这提供了从 .NET 到具有最长支持窗口的完全发布版本的快速迁移路径。

本指南未提供适用于 .NET 9(预览版)或 .NET 6 的特定示例。 如果需要面向这些版本,可以改编 .NET 8 示例。

准备迁移

如果尚未开始,请使用Azure PowerShell确定需要在当前 Azure 订阅中迁移的应用列表。

将应用迁移到独立工作器模型之前,应全面查看本指南的内容。 你还应熟悉独立工作器模型的功能以及两个模型之间的差异

若要迁移应用程序,需要:

  1. 根据迁移本地项目中的步骤,将本地项目迁移到独立工作器模型。
  2. 迁移项目后,使用Azure Functions Core Tools版本 4.x 在本地全面测试应用。
  3. 将 Azure 中的函数应用更新到独立模型。

迁移本地项目

本节概述了为将本地项目移动到独立辅助角色模型,需要对其进行的各种更改。 某些步骤会根据 .NET 的目标版本而更改。 使用选项卡选择与所需版本匹配的说明。 这些步骤假定使用本地 C# 项目,并且如果应用改用 C# 脚本(.csx 文件),则应 先转换为项目模型,然后再继续操作。

提示

如果要迁移到 .NET 的 LTS 或 STS 版本,可使用 .NET 升级助手自动进行以下部分提到的许多更改。

首先,转换项目文件并更新依赖项。 执行此操作时,你将看到项目的生成错误。 在后续步骤中,你将进行相应的更改以消除这些错误。

项目文件

以下示例是在版本 4.x 上使用 .NET 6 的 .csproj 项目文件:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    <RootNamespace>My.Namespace</RootNamespace>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.1" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

使用以下过程之一将此 XML 文件更新为在独立辅助角色模型中运行:

这些步骤假定使用本地 C# 项目,并且如果应用改用 C# 脚本(.csx 文件),则应 先转换为项目模型,然后再继续操作。

需要在 .csproj XML 项目文件中进行以下更改:

  1. 设置 PropertyGroup 的值。将 TargetFramework 指定为 net8.0

  2. 设置 PropertyGroup 的值。将 AzureFunctionsVersion 指定为 v4

  3. 将以下 OutputType 元素添加到 PropertyGroup

    <OutputType>Exe</OutputType>
    
  4. 替换现有 ItemGroupPackageReference 列表,包含以下 ItemGroup

    <ItemGroup>
      <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.19.0" />
      <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.14.1" />
    </ItemGroup>
    
  5. 添加以下新的 ItemGroup

    <ItemGroup>
      <Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext"/>
    </ItemGroup>
    

进行这些更改后,更新的项目应如以下示例所示:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    <RootNamespace>My.Namespace</RootNamespace>
    <OutputType>Exe</OutputType>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.18.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.13.0" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
  <ItemGroup>
    <Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext"/>
  </ItemGroup>
</Project>

更改项目的目标框架可能还需要更改项目代码之外的工具链部分。 例如,在 VS Code 中,可能需要通过用户设置或项目的 .vscode/settings.json 文件更新 azureFunctions.deploySubpath 扩展设置。 作为生成步骤或 CI/CD 管道的一部分,检查是否存在对框架版本的任何依赖项,这些依赖项可能存在于项目代码之外。

包引用

迁移到独立辅助角色模型时,需要更改应用程序引用的包。

更新项目以引用以下项的最新稳定版本:

根据应用使用的触发器和绑定,应用可能需要引用一组额外的包。 有关要考虑的扩展列表,请参阅受支持的绑定;有关独立进程模型的完整安装说明,请参阅每个扩展的文档。 这些扩展的包都将位于Microsoft.Azure.Functions.Worker.Extensions 前缀下。 必须安装任何目标包的最新稳定版本。

应用程序不应引用Microsoft.Azure.WebJobs.*命名空间中的任何包。 如果有对这些内容的任何剩余引用,应将其删除。

提示

应用还可能依赖于 Azure SDK 类型,无论是作为触发器和绑定的一部分,还是作为独立的依赖项。 还应利用此机会升级这些内容。 最新版本的 Functions 扩展适用于最新版本的Azure SDK for .NET,这些内容几乎所有的包都是Azure.*形式。

Program.cs 文件

当迁移以在独立的工作进程中运行时,必须将包含以下内容的 Program.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();
    })
    .Build();

host.Run();

此示例包括 ASP.NET Core 集成以提高性能,并在应用使用 HTTP 触发器时提供熟悉的编程模型。 如果不打算使用 HTTP 触发器,则可以将对 ConfigureFunctionsWebApplication 的调用替换为对 ConfigureFunctionsWorkerDefaults 的调用。 如果这样做,则可以从项目文件中删除对 Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore 的引用。 但是,为了获得最佳性能,即使对于具有其他触发器类型的函数,也应保留对 ASP.NET Core 的 FrameworkReference

Program.cs 文件将替换任何具有 FunctionsStartup 属性的文件(通常是 Startup.cs 文件)。 在 FunctionsStartup 代码将引用 IFunctionsHostBuilder.Services 的位置,可以改为在 Program.csHostBuilder.ConfigureServices() 方法中添加语句。 若要了解有关使用 Program.cs 的详细信息,请参阅独立工作器模型指南中的启动和配置

上面默认的 Program.cs 示例包括独立辅助角色模型的 Application Insights 集成设置。 在你的 Program.cs 中,还必须配置任何应该应用于项目中代码的日志的日志筛选。 在隔离的辅助角色模型中,host.json 文件仅控制 Functions 主机运行时发出的事件。 如果未在 Program.cs 中配置筛选规则,则可能会看到遥测中存在各种类别的日志级别差异。

尽管可以将自定义配置源注册为 HostBuilder 中的一部分,但请注意,这些源同样仅适用于你的项目中的代码。 平台还需要触发器和绑定配置,这应通过应用程序设置Key Vault 引用应用程序配置引用功能提供。

将任何现有 FunctionsStartup 中的所有内容移动到 Program.cs 文件后,便可以删除 FunctionsStartup 属性及其应用到的类。

函数签名更改

某些关键类型在进程内模型和独立辅助角色模型之间发生更改。 其中许多都与构成函数签名的属性、参数和返回类型相关。 对于每个函数,必须对以下各项进行更改:

  • 函数属性(也会设置函数的名称)
  • 函数如何获取 ILogger/ILogger<T>
  • 触发器和绑定属性和参数

本部分的其余部分将指导你完成这些步骤。

函数特性

在独立工作器模型中,Function 属性取代了 FunctionName 属性。 新属性的签名相同,唯一的区别在于名称。 因此,你可以在整个项目中执行字符串替换。

Logging

在进程内模型中,可以在函数中包含可选的 ILogger 参数,也可以使用依赖项注入来获取一个 ILogger<T>。 如果你的应用已在使用依赖项注入,则相同的机制在独立工作器模型中运行。

但对于依赖于 ILogger 方法参数的任何函数,则需要进行更改。 建议使用依赖项注入来获取一个 ILogger<T>。 使用以下步骤迁移函数的日志记录机制:

  1. 在函数类中,添加一个 private readonly ILogger<MyFunction> _logger; 属性,并将 MyFunction 替换为函数类的名称。

  2. 为函数类创建一个构造函数,该函数类采用 ILogger<T> 作为参数:

    public MyFunction(ILogger<MyFunction> logger) {
        _logger = logger;
    }
    

    将上述代码片段中的两个 MyFunction 实例替换为函数类的名称。

  3. 对于函数代码中的日志记录操作,请将对参数的 ILogger 引用替换为对 _logger 的引用。

  4. 从函数签名中移除 ILogger 参数。

若要了解详细信息,请参阅独立工作器模型中的日志记录

触发器和绑定更改

在上一步中更改包引用时,你在触发器和绑定中引入了错误,现在要修复这些错误:

  1. 删除任何 using Microsoft.Azure.WebJobs; 语句。

  2. 添加一个 using Microsoft.Azure.Functions.Worker; 语句。

  3. 对于每个绑定属性,按照其参考文档中指定的方式更改属性的名称,可以在支持的绑定索引中找到该文档。 一般情况下,属性名称会更改,如下所示:

    • 触发器通常以相同的方式保持命名。 例如,这两个模型的属性名称都是 QueueTrigger
    • 输入绑定通常需要将“Input”添加到其名称中。 例如,如果你在进程内模型中使用了 CosmosDB 输入绑定属性,则该属性现在将是 CosmosDBInput
    • 输出绑定通常需要将“Output”添加到其名称中。 例如,如果在进程内模型中使用了 Queue 输出绑定属性,则该属性现在将是 QueueOutput
  4. 更新属性参数以反映独立工作器模型版本,如绑定的参考文档中指定。

    例如,在进程内模型中,Blob 输出绑定由包含 Access 属性的 [Blob(...)] 属性来表示。 在独立工作器模型中,Blob 输出属性将为 [BlobOutput(...)]。 绑定不再需要该 Access 属性,因此可以删除该参数。 因此 [Blob("sample-images-sm/{fileName}", FileAccess.Write, Connection = "MyStorageConnection")] 将变为 [BlobOutput("sample-images-sm/{fileName}", Connection = "MyStorageConnection")]

  5. 将输出绑定移出函数参数列表。 如果只有一个输出绑定,则可以将此应用到函数的返回类型。 如果有多个输出,请为每个输出创建一个具有特性的新类,并将属性应用于这些特性。 若要了解详细信息,请参阅多个输出绑定

  6. 查阅每个绑定的参考文档,以了解允许绑定到的类型。 在某些情况下,可能需要更改类型。 对于输出绑定,如果进程内模型版本使用了一个 IAsyncCollector<T>,则可以将其替换为目标类型的数组的绑定:T[]。 还可以考虑将输出绑定替换为它所表示的服务的客户端对象,或者作为输入绑定的绑定类型(如果可用),或者自己注入客户端

  7. 如果函数包含 IBinder 参数,请将其移除。 将该功能替换为它所表示的服务的客户端对象,或者作为输入绑定的绑定类型(如果可用),或者自己注入客户端

  8. 更新函数代码以使用任何新类型。

local.settings.json 文件

只有在本地运行时才使用 local.settings.json 文件。 有关信息,请参阅本地设置文件

从进程内运行迁移到在独立工作进程中运行时,需要将FUNCTIONS_WORKER_RUNTIME值更改为 "dotnet-isolated"。 确保 local.settings.json 文件至少包含以下元素:

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

你的“AzureWebJobsStorage”值可能有所不同。 无需在迁移过程中更改其值。

host.json 文件

无需对 host.json 文件进行更改。 但是,如果此文件中的 Application Insights 配置来自你的进程内模型项目,则可能需要在 Program.cs 文件中进行其他更改。 host.json 文件仅控制来自 Functions 主机运行时的日志记录,在隔离的辅助角色模型中,其中一些日志直接来自应用程序,这给了你更多控制。 有关如何筛选这些日志的详细信息,请参阅在独立辅助角色模型中管理日志级别

其他代码更改

本部分重点介绍在迁移过程中要考虑的其他代码更改。 不是所有应用程序都需要这些更改,但你应评估它们是否与你的方案相关。

JSON 序列化

默认情况下,独立辅助角色模型将 System.Text.Json 用于 JSON 序列化。 若要自定义序列化程序选项或切换到 JSON.NET (Newtonsoft.Json),请参阅这些说明

Application Insights 日志级别和筛选

日志可以从 Functions 主机运行时和你项目中的代码发送到 Application Insights。 host.json 让你可以为主机日志记录配置规则,但要控制来自你的代码的日志,需要将筛选规则配置为你的 Program.cs 的一部分。 有关如何筛选这些日志的详细信息,请参阅在独立辅助角色模型中管理日志级别

函数迁移示例

HTTP 触发器示例

进程内模型的 HTTP 触发器可能如以下示例所示:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;

namespace Company.Function
{
    public static class HttpTriggerCSharp
    {
        [FunctionName("HttpTriggerCSharp")]
        public static IActionResult Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            return new OkObjectResult($"Welcome to Azure Functions, {req.Query["name"]}!");
        }
    }
}

迁移版本的 HTTP 触发器可能如以下示例所示:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace Company.Function
{
    public class HttpTriggerCSharp
    {
        private readonly ILogger<HttpTriggerCSharp> _logger;

        public HttpTriggerCSharp(ILogger<HttpTriggerCSharp> logger)
        {
            _logger = logger;
        }

        [Function("HttpTriggerCSharp")]
        public IActionResult Run(
            [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req)
        {
            _logger.LogInformation("C# HTTP trigger function processed a request.");

            return new OkObjectResult($"Welcome to Azure Functions, {req.Query["name"]}!");
        }
    }
}

更新 Azure 中的函数应用

将函数应用更新为独立模型涉及执行两个应该一起完成的更改,因为如果你只完成一个,该应用将处于错误状态。 这两项更改还会导致应用进程重启。 出于这些原因,你应使用过渡槽执行更新。 过渡槽有助于最大程度地减少应用停机时间,并允许你在 Azure 中使用更新的配置测试和验证迁移后的代码。 然后,可以通过交换操作将完全迁移的应用部署到生产槽。

重要

当应用的已部署有效负载与所配置的运行时不匹配时,它将处于错误状态。 在迁移过程中,你将应用置于此状态,理想情况下只是暂时的。 部署槽有助于缓解此情况的影响,因为在将更改作为单个更新应用于生产环境之前,错误状态将在过渡(非生产)环境中得到解决。 这些槽还可以防御任何错误,使得您可以在进入生产环境之前检测到任何其他问题。

在此过程中,你可能仍会在日志中看到来自过渡(非生产)槽的错误。 这是意料之中的,但随着你继续执行这些步骤,这些问题应该会消失。 在执行槽交换操作之前,您应该确认这些错误不再出现,并且你的应用程序正在按预期工作。

请使用以下步骤使用部署槽将你的函数应用更新为独立工作器模型:

  1. 如果还没有部署槽,则创建一个部署槽。 你可能还想熟悉槽交换过程,并确保能够以最小的中断对现有应用程序进行更新。

  2. 通过将 FUNCTIONS_WORKER_RUNTIME 应用程序设置设定为 dotnet-isolated,将过渡(非生产)槽的配置更改为使用独立工作器模型。 FUNCTIONS_WORKER_RUNTIME 不应标记为“槽设置”。

    如果在更新过程中还以不同版本的 .NET 为目标,则还应更改堆栈配置。 为此,请参阅有关更新独立工作器模型的堆栈配置的说明。 对于你将来进行的任何 .NET 版本更新,你将使用相同的说明。

    如果你有任何自动化基础结构预配(例如 CI/CD 管道),请确保还要更新自动化,以将 FUNCTIONS_WORKER_RUNTIME 保持设置为 dotnet-isolated 并以正确的 .NET 版本为目标。

  3. 将迁移的项目发布到函数应用的过渡(非生产)槽。

    如果你使用 Visual Studio 将独立工作器模型项目发布到使用进程内模型的现有应用或槽,则它还可以为你完成上一步。 如果你未完成上一步,则 Visual Studio 在部署期间会提示你更新函数应用。 Visual Studio 将此呈现为单个操作,但这些操作仍然是两个单独的操作。 在过渡状态期间,你在日志中可能仍会看到来自过渡(非生产)槽的错误。

  4. 确认你的应用程序在过渡(非生产)槽内按预期工作。

  5. 执行槽交换操作。 这会将你在过渡(非生产)槽中所做的更改应用于生产槽。 槽交换作为单个更新发生,这可避免在生产环境中引入过渡错误状态。

  6. 确认你的应用程序在生产槽内按预期工作。

完成这些步骤后,迁移便已完成,并且你的应用在独立模型中运行。 祝贺你! 对于需要迁移的任何其他应用,请根据需要重复本指南中的步骤。

后续步骤