Bilješka
Pristup ovoj stranici zahtijeva provjeru vjerodostojnosti. Možete pokušati da se prijavite ili promijenite direktorije.
Pristup ovoj stranici zahtijeva provjeru vjerodostojnosti. Možete pokušati promijeniti direktorije.
This guide walks you through migrating your .NET Durable Functions app from the in-process model to the isolated worker model. The in-process model reaches end of support on November 10, 2026. After that date, no security updates or bug fixes are provided. The isolated worker model also gives you full process control, standard .NET dependency injection, and access to the latest platform features.
Warning
Support for the in-process model ends on November 10, 2026. We recommend migrating now. For background on the isolated worker model, see .NET isolated worker process overview.
Migration checklist
Use the following checklist to track your progress through each migration step:
| Step | Section |
|---|---|
| 1. Verify prerequisites | Prerequisites |
| 2. Update the project file | Update the project file |
| 3. Add Program.cs | Add Program.cs |
| 4. Update package references | Update package references |
| 5. Update function code | Update function code |
| 6. Update local.settings.json | Update local.settings.json |
| 7. Test locally | Test locally |
| 8. Deploy to Azure | Deploy to Azure |
Prerequisites
- Azure Functions Core Tools v4.x or later
- .NET 8.0 SDK (or your target .NET version)
- Visual Studio 2022 or VS Code with Azure Functions extension
Identify apps to migrate (optional)
If you're not sure which apps still use the in-process model, run this Azure PowerShell script:
$FunctionApps = Get-AzFunctionApp
$AppInfo = @{}
foreach ($App in $FunctionApps)
{
if ($App.Runtime -eq 'dotnet')
{
$AppInfo.Add($App.Name, $App.Runtime)
}
}
$AppInfo
Apps that show dotnet as the runtime use the in-process model. Apps that show dotnet-isolated already use the isolated worker model.
Update the project file
Before (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>
After (isolated worker)
<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>
The main changes are switching to an executable output type and replacing all Microsoft.Azure.WebJobs.* packages with their Microsoft.Azure.Functions.Worker.* equivalents.
Add Program.cs
The isolated worker model requires a Program.cs entry point. Create this file in your project root. If you have a FunctionsStartup class in Startup.cs, move those service registrations into the ConfigureServices block and delete 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();
Update package references
Durable Functions package mapping
| In-process package | Isolated worker package |
|---|---|
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 |
Common extension package mapping
| In-process | Isolated worker |
|---|---|
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 |
Important
Remove any references to Microsoft.Azure.WebJobs.* namespaces and Microsoft.Azure.Functions.Extensions from your project.
Update function code
This section covers the code changes for each Durable Functions type. Jump to the section for the function types your app uses:
- Namespace changes
- Orchestrator functions
- Activity functions
- Client functions
- Retry policies (if used)
- Entity functions (if used)
For a complete API-by-API mapping, see the API reference.
Namespace changes
// 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;
Function attribute changes
// Before (In-Process)
[FunctionName("MyOrchestrator")]
// After (Isolated Worker)
[Function(nameof(MyOrchestrator))]
Orchestrator function changes
Before (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 };
}
After (Isolated Worker):
[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 };
}
Key differences
| Aspect | In-Process | Isolated Worker |
|---|---|---|
| Context type | IDurableOrchestrationContext |
TaskOrchestrationContext |
| Logger | ILogger parameter |
context.CreateReplaySafeLogger() |
| Attribute | [FunctionName] |
[Function] |
Activity function changes
Before (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;
}
After (Isolated Worker):
[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;
}
Client function changes
Before (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);
}
After (Isolated Worker):
[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);
}
Client type changes
| In-process | Isolated worker |
|---|---|
IDurableOrchestrationClient |
DurableTaskClient |
StartNewAsync() |
ScheduleNewOrchestrationInstanceAsync() |
CreateCheckStatusResponse() |
CreateCheckStatusResponseAsync() |
HttpRequest / IActionResult |
HttpRequestData / HttpResponseData |
Retry policy changes
In-process uses RetryOptions with CallActivityWithRetryAsync. The isolated worker uses TaskOptions with the standard CallActivityAsync.
Before (In-Process):
var retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: 3);
string result = await context.CallActivityWithRetryAsync<string>(
"MyActivity", retryOptions, input);
After (Isolated Worker):
var retryOptions = new TaskOptions(
new TaskRetryOptions(new RetryPolicy(
maxNumberOfAttempts: 3,
firstRetryInterval: TimeSpan.FromSeconds(5))));
string result = await context.CallActivityAsync<string>(
"MyActivity", input, retryOptions);
Entity function changes
Before (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;
}
}
After (Isolated Worker):
[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;
}
Breaking behavior changes
Review these changes before testing your migrated app. For the complete API-by-API mapping, see the API reference.
Warning
Serialization default changed: The isolated worker uses System.Text.Json by default instead of Newtonsoft.Json. If your orchestrations pass complex objects, test serialization carefully. See JSON serialization differences for configuration options.
Warning
ContinueAsNew default change: The preserveUnprocessedEvents parameter default changed from false (2.x) to true (isolated). If your orchestration uses ContinueAsNew and relies on unprocessed events being discarded, explicitly pass preserveUnprocessedEvents: false.
Note
RestartAsync default change: The restartWithNewInstanceId parameter default changed from true (2.x) to false (isolated). If your code calls RestartAsync and depends on a new instance ID being generated, explicitly pass restartWithNewInstanceId: true.
Other notable changes:
- Entity proxies removed —
CreateEntityProxy<T>isn't available. UseEntities.CallEntityAsyncorEntities.SignalEntityAsyncdirectly. - Cross-task-hub operations removed — Overloads that accepted
taskHubName/connectionNamearen't available. Only same-task-hub operations are supported. - Orchestration history moved —
DurableOrchestrationStatus.Historyis no longer on the status object. UseDurableTaskClient.GetOrchestrationHistoryAsync.
Update local.settings.json
The key change is setting FUNCTIONS_WORKER_RUNTIME from dotnet to dotnet-isolated:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
Note
Your storage backend configuration (Azure Storage, MSSQL, Netherite, or Durable Task Scheduler) is unchanged by the migration. Keep your existing storage-related settings.
Test locally
Run your function app locally and verify all orchestrations, activities, and entities work correctly.
func start
Verify functionality
Test the following scenarios as applicable:
- Start an orchestration with an HTTP trigger
- Monitor the orchestration status
- Verify the activity execution order
- Test entity operations if applicable
- Check Application Insights telemetry
Deploy to Azure
Recommended: Use deployment slots
Use deployment slots to minimize downtime:
- Create a staging slot for your function app.
- Update staging slot configuration:
- Set
FUNCTIONS_WORKER_RUNTIMEtodotnet-isolated. - Update .NET stack version if needed.
- Set
- Deploy migrated code to the staging slot.
- Test thoroughly in the staging slot.
- Perform slot swap to move changes to production.
Update application settings
In the Azure portal or via CLI:
az functionapp config appsettings set \
--name <FUNCTION_APP_NAME> \
--resource-group <RESOURCE_GROUP> \
--settings FUNCTIONS_WORKER_RUNTIME=dotnet-isolated
Update stack configuration
If targeting a different .NET version:
az functionapp config set \
--name <FUNCTION_APP_NAME> \
--resource-group <RESOURCE_GROUP> \
--net-framework-version v8.0
Common migration issues
Issue: Assembly load errors
Symptom: Could not load file or assembly errors.
Solution: Ensure you remove all Microsoft.Azure.WebJobs.* package references and replace them with isolated worker equivalents.
Issue: Binding attribute not found
Symptom: The type or namespace 'QueueTrigger' could not be found
Solution: Add the appropriate extension package and update using statements:
// Add using statement
using Microsoft.Azure.Functions.Worker;
// Install package
// dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues
Issue: IDurableOrchestrationContext not found
Symptom: The type or namespace 'IDurableOrchestrationContext' could not be found
Solution: Replace with TaskOrchestrationContext:
using Microsoft.DurableTask;
[Function(nameof(MyOrchestrator))]
public static async Task MyOrchestrator([OrchestrationTrigger] TaskOrchestrationContext context)
{
// ...
}
Issue: JSON serialization differences
Symptom: Serialization errors or unexpected data formats
Solution: The isolated model uses System.Text.Json by default. Configure serialization in Program.cs:
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureServices(services => {
services.Configure<JsonSerializerOptions>(options => {
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
})
.Build();
To use Newtonsoft.Json instead:
services.Configure<WorkerOptions>(options => {
options.Serializer = new NewtonsoftJsonObjectSerializer();
});
Issue: Migrating custom serialization settings
Symptom: You used IMessageSerializerSettingsFactory in the in-process model and need the equivalent in isolated worker.
Solution: Configure the worker-level serializer in Program.cs. For details, see the behavioral changes section of the API reference and Serialization and persistence in Durable Functions.
To use Newtonsoft.Json with custom settings:
// 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();
Note
This approach requires the Newtonsoft.Json and Azure.Core.Serialization NuGet packages.
Checklist
Use this checklist to ensure a complete migration:
- Updated project file with
<OutputType>Exe</OutputType> - Replaced
Microsoft.NET.Sdk.Functionswith worker packages - Replaced
Microsoft.Azure.WebJobs.Extensions.DurableTaskwith isolated package - Created
Program.cswith host configuration - Removed
FunctionsStartupclass (if present) - Updated all
[FunctionName]to[Function] - Replaced
IDurableOrchestrationContextwithTaskOrchestrationContext - Replaced
IDurableOrchestrationClientwithDurableTaskClient - Updated logging to use DI or
FunctionContext - Updated
local.settings.jsonwithdotnet-isolatedruntime - Removed all
Microsoft.Azure.WebJobs.*using statements - Added
Microsoft.Azure.Functions.Workerusing statements - Replaced
CreateEntityProxy<T>with directCallEntityAsync/SignalEntityAsynccalls - Replaced cross-task-hub operation overloads (if used)
- Replaced batch
GetStatusAsync/PurgeInstanceHistoryAsyncby-ID calls with filter-based or individual calls - Migrated
DurableOrchestrationStatus.Historyaccess toGetOrchestrationHistoryAsync - Updated entity
DispatchAsyncconstructor params to use DI - Tested all functions locally
- Deployed to staging slot and verified
- Swapped to production
Next steps
- In-process to isolated worker API mapping — complete API reference for your migration
- Durable Functions overview for .NET isolated worker
- Durable Functions versions and migration guide