Memigrasikan aplikasi Durable Functions Anda dari dalam proses ke model pekerja terisolasi (.NET)

Panduan ini memandu Anda memigrasikan aplikasi .NET Durable Functions dari model dalam proses ke model pekerja yang terisolasi. Model dalam proses mencapai akhir dukungan pada 10 November 2026. Setelah tanggal tersebut, tidak ada pembaruan keamanan atau perbaikan bug yang disediakan. Model pekerja yang terisolasi juga memberi Anda kontrol proses penuh, injeksi dependensi .NET standar, dan akses ke fitur platform terbaru.

Warning

Dukungan untuk model dalam proses berakhir pada 10 November 2026. Kami menyarankan untuk migrasi sekarang. Untuk latar belakang model pekerja terisolasi, lihat gambaran umum proses pekerja .NET terisolasi.

Daftar periksa migrasi

Gunakan daftar periksa berikut untuk melacak kemajuan Anda melalui setiap langkah migrasi:

Step Section
1. Verifikasi prasyarat Prasyarat
2. Perbarui file proyek Memperbarui file proyek
3. Tambahkan Program.cs Tambahkan Program.cs
4. Memperbarui referensi paket Memperbarui referensi paket
5. Perbarui kode fungsi Memperbarui kode fungsi
6. Perbarui local.settings.json Memperbarui local.settings.json
7. Uji secara lokal Uji secara lokal
8. Sebarkan ke Azure Sebarkan ke Azure

Prasyarat

  • Azure Functions Core Tools v4.x atau yang lebih baru
  • .NET 8.0 SDK (atau versi .NET target Anda)
  • Visual Studio 2022 atau VS Code dengan ekstensi Azure Functions

Mengidentifikasi aplikasi untuk dimigrasikan (opsional)

Jika Anda tidak yakin aplikasi mana yang masih menggunakan model dalam proses, jalankan skrip Azure PowerShell ini:

$FunctionApps = Get-AzFunctionApp

$AppInfo = @{}

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

$AppInfo

Aplikasi yang menampilkan dotnet sebagai waktu proses menggunakan model in-process. Aplikasi yang menunjukkan dotnet-isolated sudah menggunakan model pekerja yang terisolasi.

Memperbarui file proyek

Sebelum (dalam proses)

<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>

Setelah (pekerja terisolasi)

<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>

Perubahan utama adalah beralih ke jenis output yang dapat dieksekusi dan mengganti semua paket Microsoft.Azure.WebJobs.* dengan paket Microsoft.Azure.Functions.Worker.* yang setara.

Tambahkan Program.cs

Model pekerja terisolasi memerlukan Program.cs titik masuk. Buat file ini di akar proyek Anda. Jika Anda memiliki FunctionsStartup kelas di Startup.cs, pindahkan blok pendaftaran layanan tersebut ke ConfigureServices dan hapus 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();

Memperbarui referensi paket

pemetaan paket Durable Functions

Paket dalam proses Paket pekerja terisolasi
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

Pemetaan paket ekstensi umum

Sedang dalam proses Pekerja terisolasi
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

Hapus referensi apa pun ke namespace Microsoft.Azure.WebJobs.* dan Microsoft.Azure.Functions.Extensions dari proyek Anda.

Memperbarui kode fungsi

Bagian ini mencakup perubahan kode untuk setiap jenis Durable Functions. Lompat ke bagian untuk jenis fungsi yang digunakan aplikasi Anda:

Untuk pemetaan API-by-API lengkap, lihat referensi API.

Perubahan namespace

// 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;

Perubahan atribut fungsi

// Before (In-Process)
[FunctionName("MyOrchestrator")]

// After (Isolated Worker)
[Function(nameof(MyOrchestrator))]

Perubahan fungsi orkestrator

Sebelum (Dalam Proses):

[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 };
}

Setelah (Pekerja Terisolasi):

[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 };
}

Perbedaan utama

Aspek Sedang Diproses Pekerja Terisolasi
Jenis konteks IDurableOrchestrationContext TaskOrchestrationContext
Logger ILogger parameter context.CreateReplaySafeLogger()
Atribut [FunctionName] [Function]

Perubahan pada fungsi aktivitas

Sebelum (Dalam Proses):

[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;
}

Setelah (Pekerja Terisolasi):

[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;
}

Perubahan fungsi klien

Sebelum (Dalam Proses):

[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);
}

Setelah (Pekerja Terisolasi):

[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);
}

Perubahan jenis klien

Sedang dalam proses Pekerja terisolasi
IDurableOrchestrationClient DurableTaskClient
StartNewAsync() ScheduleNewOrchestrationInstanceAsync()
CreateCheckStatusResponse() CreateCheckStatusResponseAsync()
HttpRequest / IActionResult HttpRequestData / HttpResponseData

Mencoba kembali perubahan kebijakan

Proses ini menggunakan RetryOptions dengan CallActivityWithRetryAsync. Pekerja yang terisolasi menggunakan TaskOptions dengan standar CallActivityAsync.

Sebelum (Dalam Proses):

var retryOptions = new RetryOptions(
    firstRetryInterval: TimeSpan.FromSeconds(5),
    maxNumberOfAttempts: 3);

string result = await context.CallActivityWithRetryAsync<string>(
    "MyActivity", retryOptions, input);

Setelah (Pekerja Terisolasi):

var retryOptions = new TaskOptions(
    new TaskRetryOptions(new RetryPolicy(
        maxNumberOfAttempts: 3,
        firstRetryInterval: TimeSpan.FromSeconds(5))));

string result = await context.CallActivityAsync<string>(
    "MyActivity", input, retryOptions);

Perubahan fungsi entitas

Sebelum (Dalam Proses):

[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;
    }
}

Setelah (Pekerja Terisolasi):

[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;
}

Perubahan perilaku yang berdampak signifikan

Tinjau perubahan ini sebelum menguji aplikasi yang dimigrasikan. Untuk pemetaan API-by-API lengkap, lihat referensi API.

Warning

Default serialisasi berubah: Pekerja yang terisolasi menggunakan System.Text.Json secara default alih-alih Newtonsoft.Json. Jika orkestrasi Anda melewati objek yang kompleks, uji serialisasi dengan hati-hati. Lihat Perbedaan serialisasi JSON untuk opsi konfigurasi.

Warning

Perubahan default ContinueAsNew: Parameter preserveUnprocessedEvents default berubah dari false (2.x) menjadi true (terisolasi). Jika orkestrasi Anda menggunakan ContinueAsNew dan mengandalkan pembuangan peristiwa yang tidak diproses, operasikan secara eksplisit preserveUnprocessedEvents: false.

Note

Perubahan default RestartAsync: Parameter restartWithNewInstanceId default berubah dari true (2.x) menjadi false (terisolasi). Jika kode Anda memanggil RestartAsync dan bergantung pada ID instans baru yang dihasilkan, berikan secara eksplisit restartWithNewInstanceId: true.

Perubahan penting lainnya:

  • Proksi entitas dihapusCreateEntityProxy<T> tidak tersedia. Gunakan Entities.CallEntityAsync atau Entities.SignalEntityAsync secara langsung.
  • Operasi hub lintas tugas dihapus — Kelebihan beban yang diterima taskHubName/connectionName tidak tersedia. Hanya operasi hub tugas yang sama yang didukung.
  • Riwayat orkestrasi telah dipindahkanDurableOrchestrationStatus.History tidak lagi terdapat dalam objek status. Gunakan DurableTaskClient.GetOrchestrationHistoryAsync.

Memperbarui local.settings.json

Perubahan kunci adalah pengaturan FUNCTIONS_WORKER_RUNTIME dari dotnet ke dotnet-isolated:

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

Note

Konfigurasi backend penyimpanan Anda (Azure Storage, MSSQL, Netherite, atau Durable Task Scheduler) tidak berubah oleh migrasi. Pertahankan pengaturan terkait penyimpanan yang ada.

Uji secara lokal

Jalankan aplikasi fungsi Anda secara lokal dan verifikasi semua orkestrasi, aktivitas, dan entitas berfungsi dengan benar.

func start

Memverifikasi fungsionalitas

Uji skenario berikut sebagaimana berlaku:

  1. Memulai orkestrasi dengan pemicu HTTP
  2. Pemantauan status dari orkestrasi
  3. Memverifikasi urutan eksekusi aktivitas
  4. Menguji operasi entitas jika berlaku
  5. Periksa telemetri pada Application Insights

Sebarkan ke Azure

Gunakan slot penyebaran untuk meminimalkan waktu henti:

  1. Buat slot pengujian aplikasi fungsi Anda.
  2. Memperbarui konfigurasi slot penahapan:
    • Atur FUNCTIONS_WORKER_RUNTIME ke dotnet-isolated.
    • Perbarui versi tumpukan .NET jika diperlukan.
  3. Sebarkan kode yang dimigrasikan ke slot staging.
  4. Uji secara menyeluruh di slot penahapan.
  5. Lakukan pertukaran slot untuk memindahkan perubahan ke produksi.

Memperbarui pengaturan aplikasi

Di portal Azure atau melalui CLI:

az functionapp config appsettings set \
    --name <FUNCTION_APP_NAME> \
    --resource-group <RESOURCE_GROUP> \
    --settings FUNCTIONS_WORKER_RUNTIME=dotnet-isolated

Memperbarui konfigurasi tumpukan

Jika menargetkan versi .NET yang berbeda:

az functionapp config set \
    --name <FUNCTION_APP_NAME> \
    --resource-group <RESOURCE_GROUP> \
    --net-framework-version v8.0

Masalah migrasi umum

Masalah: Kesalahan pemuatan rakitan

Gejala:Could not load file or assembly Kesalahan.

Solusi: Pastikan Anda menghapus semua referensi paket Microsoft.Azure.WebJobs.* dan menggantinya dengan pekerja terisolasi yang setara.

Masalah: Atribut pengikatan tidak ditemukan

Gejala:The type or namespace 'QueueTrigger' could not be found

Solusi: Tambahkan paket ekstensi yang sesuai dan perbarui menggunakan pernyataan:

// Add using statement
using Microsoft.Azure.Functions.Worker;

// Install package
// dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues

Masalah: IDurableOrchestrationContext tidak ditemukan

Gejala:The type or namespace 'IDurableOrchestrationContext' could not be found

Solusi: Ganti dengan TaskOrchestrationContext:

using Microsoft.DurableTask;

[Function(nameof(MyOrchestrator))]
public static async Task MyOrchestrator([OrchestrationTrigger] TaskOrchestrationContext context)
{
    // ...
}

Masalah: Perbedaan serialisasi JSON

Gejala: Kesalahan serialisasi atau format data yang tidak terduga

Solusi: Model terisolasi menggunakan System.Text.Json secara default. Konfigurasikan serialisasi di Program.cs:

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services => {
        services.Configure<JsonSerializerOptions>(options => {
            options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        });
    })
    .Build();

Untuk menggunakan Newtonsoft.Json sebagai gantinya:

services.Configure<WorkerOptions>(options => {
    options.Serializer = new NewtonsoftJsonObjectSerializer();
});

Masalah: Memigrasikan pengaturan serialisasi kustom

Gejala: Anda menggunakan IMessageSerializerSettingsFactory dalam model dalam proses dan memerlukan yang setara dalam pekerja terisolasi.

Solusi: Konfigurasikan serializer tingkat pekerja di Program.cs. Untuk detailnya, lihat bagian perubahan perilaku referensi API dan Serialisasi serta persistensi dalam Durable Functions.

Untuk menggunakan Newtonsoft.Json dengan pengaturan kustom:

// 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

Pendekatan ini memerlukan paket NuGet Newtonsoft.Json dan Azure.Core.Serialization.

Checklist

Gunakan daftar periksa ini untuk memastikan migrasi lengkap:

  • File proyek yang diperbarui dengan <OutputType>Exe</OutputType>
  • Mengganti Microsoft.NET.Sdk.Functions dengan paket worker
  • Mengganti Microsoft.Azure.WebJobs.Extensions.DurableTask dengan paket terisolasi
  • Dibuat Program.cs menggunakan konfigurasi host
  • Kelas yang dihapus FunctionsStartup (jika ada)
  • Diperbarui semua [FunctionName] ke [Function]
  • Diganti IDurableOrchestrationContext dengan TaskOrchestrationContext
  • Diganti IDurableOrchestrationClient dengan DurableTaskClient
  • Pengelogan yang diperbarui untuk menggunakan DI atau FunctionContext
  • Diperbarui local.settings.json dengan dotnet-isolated runtime
  • Menghapus semua Microsoft.Azure.WebJobs.* menggunakan pernyataan
  • Menambahkan Microsoft.Azure.Functions.Worker menggunakan pernyataan
  • Diganti CreateEntityProxy<T> dengan panggilan langsung CallEntityAsync/SignalEntityAsync
  • Penggantian beban overload operasi cross-task-hub (jika digunakan)
  • Mengganti panggilan berbasis ID batch GetStatusAsync/PurgeInstanceHistoryAsync dengan panggilan berbasis filter atau berbasis individu
  • Pemindahan akses DurableOrchestrationStatus.History ke GetOrchestrationHistoryAsync
  • Param konstruktor entitas DispatchAsync yang diperbarui untuk menggunakan DI
  • Menguji semua fungsi secara lokal
  • Telah diterapkan ke slot staging dan diverifikasi
  • Dipindahkan ke produksi

Langkah berikutnya