Azure Functions 如何從 In-Process 遷移至 Isolated Worker 模型
Migrate Azure Functions from In-Process to the Isolated Worker Model
指南説明如何將 Azure Functions 的 C# 應用程式由 in-process 模型遷移至 isolated worker 模型;in-process 模型的支援將於 2026 年 11 月 10 日結束。
Originally published on EpiFive, where it is kept up to date.
Support for the Azure Functions in-process model ends on November 10, 2026. To keep a C# function app supported, convert it to the isolated worker model: replace the project SDK and packages, add a Program.cs that builds the host, update function signatures and bindings, then change FUNCTIONS_WORKER_RUNTIME to dotnet-isolated and publish the new code together through a staging slot swap.
This guide follows Microsoft's migration article and adds the checks and failure modes you are likely to hit along the way. It was checked against the Microsoft Learn pages listed in the References section on 2026-10-27.
Who this is for and what you will have at the end
This article is for developers and IT administrators who own one or more .NET function apps that use the in-process model, meaning a class library with the FunctionName attribute and the Microsoft.NET.Sdk.Functions package. You do not need to know the isolated worker model already, but you should be able to build and run a function app locally.
By the end you will have a list of the in-process apps in your subscription, a converted project that builds and runs under Azure Functions Core Tools, and a deployment procedure that avoids a production outage between the runtime setting change and the code deployment.
The model differences matter because the isolated worker runs your code in a separate process from the Functions host. You control start-up, dependency injection and middleware yourself, in the same style as an ASP.NET Core app.
Prerequisites and decisions
Before you touch code, settle the following.
| Item | What to check |
|---|---|
| Functions runtime version | The guide assumes version 4.x. Apps on 1.x, 2.x or 3.x use the version 4.x migration guides first. |
| Target framework | Target .NET 10 if your app and its dependencies run on modern .NET. Target .NET Framework 4.8 only if you depend on libraries that exist only there. |
| Hosting plan | .NET 10 apps cannot run on Linux in the Consumption plan. On Linux, use the Flex Consumption plan. |
| Deployment slot | Slots are not currently supported on Flex Consumption. Test in a non-production app and see the site update strategies page for rolling updates. |
| Tooling | Install the .NET SDK for your target version and Azure Functions Core Tools 4.x. |
Check the runtime version of each app. In the Azure portal, open the function app, then Settings > Configuration, and look at the Runtime version value on the Function runtime settings tab. With the Azure CLI, list the app settings and read FUNCTIONS_EXTENSION_VERSION:
az functionapp config appsettings list --name "<FUNCTION_APP_NAME>" --resource-group "<RESOURCE_GROUP_NAME>"
A value of ~4 means the app is already on version 4.x. If it is not, follow the dedicated version migration guides instead of only changing that setting.
Step 1: Find the apps that need migrating
Microsoft's guide provides an Azure PowerShell script that lists function apps in the current subscription whose runtime is dotnet. That value identifies in-process apps. The script uses whichever subscription Azure PowerShell is currently set to, and you change it with Set-AzContext.
Set-AzContext -Subscription '<YOUR SUBSCRIPTION ID>'
$FunctionApps = Get-AzFunctionApp
$AppInfo = @{}
foreach ($App in $FunctionApps)
{
if ($App.Runtime -eq 'dotnet')
{
$AppInfo.Add($App.Name, $App.Runtime)
}
}
$AppInfo
Run it once per subscription. Record each app name, its plan type and which repository holds its code. Apps with no known owner or repository are the biggest risk, because you cannot rebuild them. If you manage subscriptions through a landing zone design, the subscription layout described in A practical Azure landing zone for small and mid-size companies helps you decide where to run the script.
Step 2: Convert the project file
Start with the project file, then follow the compiler errors. The migration guide recommends doing the .csproj and package changes first because the resulting build errors point at every remaining change.
A typical in-process project on .NET 8 looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.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>
For .NET 10, make these changes:
- Set the
Sdkattribute on theProjectelement toAzure.Functions.Sdk/1.0.0. - Set
TargetFrameworktonet10.0. - Remove the
AzureFunctionsVersionproperty. The new SDK defaults to Functions runtime version 4.x. - Replace the
Microsoft.NET.Sdk.Functionspackage reference with the worker packages. - Add an
ItemGroupwith aUsingalias forSystem.Threading.ExecutionContext.
The result from Microsoft's guide:
<Project Sdk="Azure.Functions.Sdk/1.0.0">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>My.Namespace</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.52.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.1.0" />
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.2.0" />
<!-- Other packages may also be in this list -->
</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>
The version numbers above are the ones printed in the guide on the date checked. Use the latest stable versions of Microsoft.Azure.Functions.Worker and Azure.Functions.Sdk when you do the work. For .NET Framework 4.8, the guide uses net48, the Microsoft.Azure.Functions.Worker.Extensions.Http package instead of the ASP.NET Core one, and ConfigureFunctionsWorkerDefaults in Program.cs.
If you target .NET 10 and want the tool to do part of the work, the guide notes that the .NET Upgrade Assistant can make many of these changes automatically.
Also check anything outside the project that mentions the framework version, such as build steps, CI/CD pipelines and, in Visual Studio Code, the azureFunctions.deploySubpath setting.
Step 3: Replace the binding packages
Your isolated project must not reference any package in the Microsoft.Azure.WebJobs.* namespaces or Microsoft.Azure.Functions.Extensions. Replace each with its worker equivalent. Some of the common ones from the guide:
| In-process reference | Isolated replacement |
|---|---|
Microsoft.Azure.WebJobs.Extensions.Storage.Blobs |
Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs |
Microsoft.Azure.WebJobs.Extensions.Storage.Queues |
Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues |
Microsoft.Azure.WebJobs.Extensions.Tables |
Microsoft.Azure.Functions.Worker.Extensions.Tables |
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.CosmosDB |
Microsoft.Azure.Functions.Worker.Extensions.CosmosDB |
Microsoft.Azure.WebJobs.Extensions.DurableTask |
Microsoft.Azure.Functions.Worker.Extensions.DurableTask |
Microsoft.Azure.Functions.Extensions |
Remove it. Dependency injection and start-up configuration are built in. |
The timer trigger is a special case: add Microsoft.Azure.Functions.Worker.Extensions.Timer. Install the latest stable version of each extension and read its isolated-process documentation, because a new extension version can require host.json changes. The Service Bus extension, for example, changed its host.json structure between versions 4.x and 5.x.
Step 4: Add Program.cs and remove Startup
The isolated worker needs an entry point. Add a Program.cs like this for .NET 10 with ASP.NET Core integration:
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();
ConfigureFunctionsWebApplication enables ASP.NET Core integration, which gives HTTP triggers the familiar HttpRequest and IActionResult types. If you have no HTTP triggers you can call ConfigureFunctionsWorkerDefaults instead and drop the Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore reference. The guide still recommends keeping the ASP.NET Core FrameworkReference for best performance.
Program.cs replaces any file carrying the FunctionsStartup attribute, usually Startup.cs. Move each builder.Services registration from that class into the ConfigureServices delegate, then delete the attribute and the class.
Be aware of two behaviours:
-
host.jsononly controls events emitted by the Functions host. Logs from your own code are filtered by rules you configure inProgram.cs. If you skip this, the log levels in Application Insights can differ from what you had before. - Custom configuration sources registered on the
HostBuilderapply only to code in your project. Trigger and binding configuration must still come from application settings, Key Vault references or App Configuration references.
The guide's default sample uses the Application Insights SDK. The isolated worker guide also describes an OpenTelemetry-based setup that calls AddOpenTelemetry().UseFunctionsWorkerDefaults() and AddOpenTelemetry().UseAzureMonitorExporter(). Pick one approach per app rather than mixing them.
Step 5: Update function signatures and bindings
Work through each function. The changes are mostly mechanical.
Attribute and logger
Replace FunctionName with Function. It has the same signature, so a find-and-replace works.
Replace the ILogger method parameter with constructor injection of ILogger<T>:
public class MyFunction
{
private readonly ILogger<MyFunction> _logger;
public MyFunction(ILogger<MyFunction> logger)
{
_logger = logger;
}
}
Then change calls from the old log parameter to _logger and remove the parameter from the signature.
Trigger and binding attributes
Remove using Microsoft.Azure.WebJobs; and add using Microsoft.Azure.Functions.Worker;. Then follow these naming rules from the guide:
- Triggers usually keep their name.
QueueTriggeris the same in both models. - Input bindings usually gain
Input. TheCosmosDBinput attribute becomesCosmosDBInput. - Output bindings usually gain
Output. TheQueueoutput attribute becomesQueueOutput.
Parameters also change. The in-process blob output [Blob("sample-images-sm/{fileName}", FileAccess.Write, Connection = "MyStorageConnection")] becomes [BlobOutput("sample-images-sm/{fileName}", Connection = "MyStorageConnection")], because the Access property is no longer needed.
Output bindings move out of the parameter list. With one output, apply the attribute to the return type. With several, create a class with one property per output and put the attributes on those properties. If you used IAsyncCollector<T>, bind to T[] instead. If you used IBinder, remove it and inject the Azure service client you need.
A before-and-after HTTP trigger
In-process:
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"]}!");
}
}
}
Isolated worker on .NET 10:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
namespace Company.Function
{
public class HttpTriggerCSharp(ILogger<HttpTriggerCSharp> 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"]}!");
}
}
}
The class is no longer static, because it receives its logger through the constructor.
Serialization and stream changes
Two behaviour changes do not produce compiler errors, so look for them on purpose.
First, the isolated worker uses System.Text.Json by default, while the in-process model used Newtonsoft.Json. Attributes such as [JsonProperty] and [JsonIgnore] from Newtonsoft are ignored, and the affected property binds to its default value without any error. Replace them with System.Text.Json equivalents such as [JsonPropertyName], or configure Newtonsoft for the layer that handles the payload.
Second, with ASP.NET Core integration, synchronous reads and writes on request and response streams can fail with InvalidOperationException: Synchronous operations are disallowed. Use ReadToEndAsync, WriteAsync, WriteStringAsync and FlushAsync, and make the method async with a Task<T> return type.
Step 6: Fix local.settings.json and test locally
Change FUNCTIONS_WORKER_RUNTIME in local.settings.json to dotnet-isolated. Your AzureWebJobsStorage value does not need to change.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
Build the project, then start it with Core Tools 4.x and trigger every function at least once. Pay particular attention to functions with output bindings, JSON payloads and timer schedules, since those are the areas where behaviour changes without a build error.
dotnet build
func start
Step 7: Cut over in Azure with a staging slot
Two changes must happen together: set FUNCTIONS_WORKER_RUNTIME to dotnet-isolated, and publish the migrated project. Each change restarts the app. Between them the configured runtime and the deployed code do not match, which causes the runtime/payload mismatch event AZFD0013. A staging slot keeps that temporary state out of production.
- Create a deployment slot if the app does not already have one.
- On the staging slot, set
FUNCTIONS_WORKER_RUNTIMEtodotnet-isolated. Do not mark this setting as a slot setting. - If the target .NET version changes, update the stack configuration on the slot too. In the portal, go to the function app, then Settings > Configuration, select the slot, and change .NET Version on the General settings tab. For Windows apps in the CLI, use
az functionapp config set --net-framework-version "v<VERSION>.0", and for Linux use--linux-fx-versionwith a value taken fromaz functionapp list-runtimes. - Publish the migrated project to the staging slot.
- Confirm the app works in the slot and that the errors from the interim state have stopped.
- Swap the slot into production. A swap applies the changes as a single update.
- Verify production.
For a Linux app, list the valid stack values first:
az functionapp list-runtimes --os linux --query "[?runtime == 'dotnet-isolated'].{Version:version, linuxFxVersion:linux_fx_version}" --output table
az functionapp config set --linux-fx-version "<LANGUAGE|VERSION>" --name "<FUNCTION_APP_NAME>" --resource-group "<RESOURCE_GROUP_NAME>" --slot "staging"
If you provision the app with infrastructure as code or a pipeline, update those definitions so they keep FUNCTIONS_WORKER_RUNTIME at dotnet-isolated and the right .NET version. Otherwise the next deployment can undo the change. If your pipeline deploys from GitHub Actions, workload identity federation for GitHub Actions and Azure removes stored secrets from that deployment path.
Updating the stack version restarts the app, and Microsoft's page says the app is unavailable for a brief period, typically 30 to 60 seconds, when you do it directly on production.
Verification
After the swap, check these items:
- The app status on Overview is Running.
-
az functionapp show --name "<FUNCTION_APP_NAME>" --resource-group "<RESOURCE_GROUP_NAME>" --query "siteConfig.netFrameworkVersion" --output tsvreturns the .NET version you expect. -
FUNCTIONS_WORKER_RUNTIMEreadsdotnet-isolatedin the app settings of the production slot. - Each trigger fires, and queues, topics or tables show messages being consumed.
- Application Insights shows traces and requests, with log levels you recognise.
- Re-run the PowerShell script from step 1. The app should no longer appear in the
dotnetlist.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
AZFD0013 in logs |
The setting and the deployed payload do not match | Align FUNCTIONS_WORKER_RUNTIME with the payload. For this migration, set dotnet-isolated and deploy the isolated project, using a slot. |
InvalidOperationException: Synchronous operations are disallowed |
Synchronous stream I/O under ASP.NET Core integration | Switch to the asynchronous stream methods and make the function async. |
| Properties arrive as default values | Newtonsoft attributes ignored by System.Text.Json
|
Use [JsonPropertyName] or configure Newtonsoft as the serializer. |
| Function app does not start | Dependencies or extension versions incompatible with the target framework | Check log stream, update the NuGet packages and extension references, and check host.json for extension changes. |
| Application Insights log volume or levels changed | Filtering rules live in Program.cs, not host.json
|
Add log filtering in Program.cs for your own code. |
| .NET 10 app will not run on Linux Consumption | Not supported on that plan | Move the app to Flex Consumption. |
Roll back by swapping the slot back to production, then correct the staging slot before trying again.
Closing checklist
- List every in-process app with the
Get-AzFunctionAppscript. - Confirm each app is on runtime version 4.x.
- Choose .NET 10 or .NET Framework 4.8 as the target.
- Switch the project SDK to
Azure.Functions.Sdk/1.0.0and replace the WebJobs packages. - Add
Program.cs, then delete theFunctionsStartupclass. - Update
Functionattributes, loggers, bindings and output return types. - Review serializer attributes and synchronous stream calls.
- Set
FUNCTIONS_WORKER_RUNTIMEtodotnet-isolatedlocally, then test every function. - Deploy to a staging slot, verify, and swap.
- Update any infrastructure as code or pipeline definitions.
- Cost review comes next: once apps are on a current stack, the Azure cost optimisation checklist is a good follow-up.
References
- Migrate C# apps from the in-process model to the isolated worker model
- Guide for running C# Azure Functions in an isolated worker process
- Update language versions in Azure Functions
- AZFD0013: The configured runtime does not match the worker runtime metadata
- Migrate apps from Azure Functions version 3.x to version 4.x
This guide was researched and drafted with AI assistance and checked against the vendor documentation listed in its References. Read the maintained version and related guides on EpiFive.
來源:DEV Community · dev.to