Bagikan melalui


Migrasi dari Orleans 3.x ke 7.0

Orleans 7.0 memperkenalkan beberapa perubahan bermanfaat, termasuk peningkatan hosting, serialisasi kustom, kekekalan, dan abstraksi biji-bijian.

Migration

Aplikasi yang ada menggunakan pengingat, aliran, atau persistensi biji-bijian tidak dapat dengan mudah dimigrasikan ke Orleans 7,0 karena perubahan cara Orleans mengidentifikasi biji-bijian dan aliran. Kami berencana untuk secara bertahap menawarkan jalur migrasi untuk aplikasi ini.

Aplikasi yang menjalankan versi Orleans sebelumnya tidak dapat ditingkatkan dengan lancar melalui peningkatan bergulir ke Orleans 7.0. Oleh karena itu, strategi peningkatan yang berbeda harus digunakan, seperti menyebarkan kluster baru dan menonaktifkan kluster sebelumnya. Orleans 7.0 mengubah protokol kawat dengan cara yang tidak kompatibel, yang berarti bahwa kluster tidak dapat berisi campuran Orleans host 7.0 dan host yang menjalankan versi sebelumnya dari Orleans.

Kami telah menghindari perubahan yang melanggar selama bertahun-tahun, bahkan di seluruh rilis utama, jadi mengapa sekarang? Ada dua alasan utama: identitas dan serialisasi. Mengenai identitas, Identitas biji-bijian dan aliran sekarang terdiri dari string, memungkinkan biji-bijian untuk mengodekan informasi jenis generik dengan benar dan memungkinkan aliran untuk memetakan lebih mudah ke domain aplikasi. Jenis biji-bijian sebelumnya diidentifikasi menggunakan struktur data kompleks yang tidak dapat mewakili butir generik, yang mengarah ke kasus sudut. Aliran diidentifikasi oleh string namespace layanan dan Guid kunci, yang sulit bagi pengembang untuk memetakan ke domain aplikasi mereka, namun efisien. Serialisasi sekarang toleran terhadap versi, yang berarti Anda dapat memodifikasi jenis Anda dengan cara yang kompatibel tertentu, mengikuti serangkaian aturan, dan yakinlah bahwa Anda dapat meningkatkan aplikasi Anda tanpa kesalahan serialisasi. Ini sangat bermasalah ketika jenis aplikasi bertahan di aliran atau penyimpanan biji-bijian. Bagian berikut merinci perubahan besar dan mendiskusikannya secara lebih rinci.

Perubahan kemasan

Jika Anda meningkatkan proyek ke Orleans 7.0, Anda harus melakukan tindakan berikut:

Tip

Orleans Semua sampel telah ditingkatkan ke Orleans 7.0 dan dapat digunakan sebagai referensi untuk perubahan apa yang dibuat. Untuk informasi selengkapnya, lihat Orleans masalah #8035 yang membuat item perubahan yang dilakukan pada setiap sampel.

Orleansglobal using arahan

Semua Orleans proyek baik secara langsung atau tidak langsung mereferensikan Microsoft.Orleans.Sdk paket NuGet. Orleans Ketika proyek telah dikonfigurasi untuk mengaktifkan penggunaan implisit (misalnya <ImplicitUsings>enable</ImplicitUsings>), Orleans namespace layanan dan Orleans.Hosting keduanya digunakan secara implisit. Ini berarti bahwa kode aplikasi Anda tidak memerlukan arahan ini.

Untuk informasi selengkapnya, lihat ImplicitUsings dan dotnet/orleans/src/Orleans. Sdk/build/Microsoft.Orleans. Sdk.targets.

Hosting

ClientBuilder Jenis telah diganti dengan UseOrleansClient metode ekstensi pada IHostBuilder. Jenisnya IHostBuilder berasal dari paket NuGet Microsoft.Extensions.Hosting . Ini berarti Anda dapat menambahkan Orleans klien ke host yang ada tanpa harus membuat kontainer injeksi dependensi terpisah. Klien terhubung ke kluster selama startup. Setelah IHost.StartAsync selesai, klien akan terhubung secara otomatis. Layanan yang ditambahkan ke IHostBuilder dimulai dalam urutan pendaftaran, jadi panggilan UseOrleansClient sebelum panggilan ConfigureWebHostDefaults akan memastikan Orleans dimulai sebelum ASP.NET Core dimulai misalnya, memungkinkan Anda untuk mengakses klien dari aplikasi ASP.NET Core Anda segera.

Jika Anda ingin meniru perilaku sebelumnya ClientBuilder , Anda dapat membuat terpisah HostBuilder dan mengonfigurasinya dengan Orleans klien. IHostBuilder dapat memiliki Orleans klien atau silo yang dikonfigurasi Orleans . Semua silo mendaftarkan instans IGrainFactory dan IClusterClient mana yang dapat digunakan aplikasi, jadi mengonfigurasi klien secara terpisah tidak perlu dan tidak didukung.

OnActivateAsync dan OnDeactivateAsync perubahan tanda tangan

Orleans memungkinkan biji-bijian untuk menjalankan kode selama aktivasi dan pennonaktifkanan. Ini dapat digunakan untuk melakukan tugas seperti membaca status dari pesan penyimpanan atau siklus hidup log. Dalam Orleans 7.0, tanda tangan metode siklus hidup ini berubah:

  • OnActivateAsync() sekarang menerima CancellationToken parameter. CancellationToken Ketika dibatalkan, proses aktivasi harus ditinggalkan.
  • OnDeactivateAsync() sekarang menerima DeactivationReason parameter dan CancellationToken parameter. menunjukkan DeactivationReason mengapa aktivasi dinonaktifkan. Pengembang diharapkan menggunakan informasi ini untuk tujuan pengelogan dan diagnostik. CancellationToken Ketika dibatalkan, proses penonaktifan harus segera diselesaikan. Perhatikan bahwa karena setiap host dapat gagal kapan saja, tidak disarankan untuk mengandalkan OnDeactivateAsync untuk melakukan tindakan penting seperti mempertahankan status kritis.

Pertimbangkan contoh berikut dari grain yang mengambil alih metode baru ini:

public sealed class PingGrain : Grain, IPingGrain
{
    private readonly ILogger<PingGrain> _logger;

    public PingGrain(ILogger<PingGrain> logger) =>
        _logger = logger;

    public override Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("OnActivateAsync()");
        return Task.CompletedTask;
    }

    public override Task OnDeactivateAsync(DeactivationReason reason, CancellationToken token)
    {
        _logger.LogInformation("OnDeactivateAsync({Reason})", reason);
        return Task.CompletedTask;
    }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

POCO Butir dan IGrainBase

Biji-bijian Orleans tidak perlu lagi mewarisi dari Grain kelas dasar atau kelas lainnya. Fungsionalitas ini disebut sebagai biji-bijian POCO . Untuk mengakses metode ekstensi seperti salah satu hal berikut:

Butir Anda harus mengimplementasikan IGrainBase atau mewarisi dari Grain. Berikut adalah contoh penerapan IGrainBase pada kelas biji-bijian:

public sealed class PingGrain : IGrainBase, IPingGrain
{
    public PingGrain(IGrainContext context) => GrainContext = context;

    public IGrainContext GrainContext { get; }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

IGrainBase juga mendefinisikan OnActivateAsync dan OnDeactivateAsync dengan implementasi default, memungkinkan biji-bijian Anda untuk berpartisipasi dalam siklus hidupnya jika diinginkan:

public sealed class PingGrain : IGrainBase, IPingGrain
{
    private readonly ILogger<PingGrain> _logger;

    public PingGrain(IGrainContext context, ILogger<PingGrain> logger)
    {
        _logger = logger;
        GrainContext = context;
    }

    public IGrainContext GrainContext { get; }

    public Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("OnActivateAsync()");
        return Task.CompletedTask;
    }

    public Task OnDeactivateAsync(DeactivationReason reason, CancellationToken token)
    {
        _logger.LogInformation("OnDeactivateAsync({Reason})", reason);
        return Task.CompletedTask;
    }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

Serialisasi

Perubahan paling memberatkan dalam Orleans 7.0 adalah pengenalan serializer yang toleran terhadap versi. Perubahan ini dilakukan karena aplikasi cenderung berkembang dan ini menyebabkan perangkap yang signifikan bagi pengembang, karena serializer sebelumnya tidak dapat mentolerir penambahan properti ke jenis yang ada. Di sisi lain, serializer fleksibel, memungkinkan pengembang untuk mewakili sebagian besar jenis .NET tanpa modifikasi, termasuk fitur seperti generik, polimorfisme, dan pelacakan referensi. Penggantian sudah lama tertunda, tetapi pengguna masih membutuhkan representasi keakuratan tinggi dari jenis mereka. Oleh karena itu, serializer pengganti diperkenalkan pada Orleans 7.0 yang mendukung representasi keakuratan tinggi dari jenis .NET sambil juga memungkinkan jenis berkembang. Serializer baru jauh lebih efisien daripada serializer sebelumnya, menghasilkan throughput end-to-end hingga 170% lebih tinggi.

Untuk informasi selengkapnya, lihat artikel berikut ini terkait dengan Orleans 7.0:

Identitas butir

Biji-bijian masing-masing memiliki identitas unik yang terdiri dari jenis biji-bijian dan kuncinya. Versi sebelumnya menggunakan Orleans jenis majemuk untuk GrainIds untuk mendukung kunci biji-bijian dari salah satu:

Ini melibatkan beberapa kompleksitas dalam hal menangani kunci biji-bijian. Identitas butir terdiri dari dua komponen: jenis dan kunci. Komponen jenis sebelumnya terdiri dari kode jenis numerik, kategori, dan 3 byte informasi jenis generik.

Identitas butir sekarang mengambil formulir type/key di mana keduanya type dan key merupakan string. Antarmuka kunci grain yang paling umum digunakan adalah IGrainWithStringKey. Ini sangat menyederhanakan cara kerja identitas biji-bijian dan meningkatkan dukungan untuk jenis biji-bijian generik.

Antarmuka biji-bijian juga sekarang diwakili menggunakan nama yang dapat dibaca manusia, bukan kombinasi kode hash dan representasi string dari parameter jenis generik apa pun.

Sistem baru lebih dapat disesuaikan dan penyesuaian ini dapat didorong oleh atribut.

  • GrainTypeAttribute(String) pada butir class menentukan bagian Jenis dari id biji-bijiannya.
  • DefaultGrainTypeAttribute(String) pada butir interface menentukan Jenis biji-bijian yang IGrainFactory harus diselesaikan secara default saat mendapatkan referensi biji-bijian. Misalnya, saat memanggil IGrainFactory.GetGrain<IMyGrain>("my-key"), pabrik biji-bijian akan mengembalikan referensi ke biji-bijian "my-type/my-key" jika IMyGrain memiliki atribut yang disebutkan di atas yang ditentukan.
  • GrainInterfaceTypeAttribute(String) memungkinkan pengesampingan nama antarmuka. Menentukan nama secara eksplisit menggunakan mekanisme ini memungkinkan penggantian nama jenis antarmuka tanpa melanggar kompatibilitas dengan referensi butir yang ada. Perhatikan bahwa antarmuka Anda juga harus memiliki AliasAttribute dalam hal ini, karena identitasnya mungkin diserialisasikan. Untuk informasi selengkapnya tentang menentukan alias jenis, lihat bagian tentang serialisasi.

Seperti disebutkan di atas, mengganti kelas grain default dan nama antarmuka untuk jenis Anda memungkinkan Anda mengganti nama jenis yang mendasar tanpa merusak kompatibilitas dengan penyebaran yang ada.

Identitas aliran

Ketika Orleans aliran pertama kali dirilis, aliran hanya dapat diidentifikasi menggunakan Guid. Ini efisien dalam hal alokasi memori, tetapi sulit bagi pengguna untuk membuat identitas aliran yang bermakna, sering memerlukan beberapa pengodean atau tidak langsung untuk menentukan identitas aliran yang sesuai untuk tujuan tertentu.

Dalam Orleans 7.0, aliran sekarang diidentifikasi menggunakan string. Orleans.Runtime.StreamId struct berisi tiga properti: , StreamId.Namespace, StreamId.Keydan StreamId.FullKey. Nilai properti ini adalah string UTF-8 yang dikodekan. Contohnya,StreamId.Create(String, String).

Penggantian SimpleMessageStreams dengan BroadcastChannel

SimpleMessageStreams (juga disebut SMS) dihapus dalam 7.0. SMS memiliki antarmuka yang sama dengan Orleans.Providers.Streams.PersistentStreams, tetapi perilakunya sangat berbeda, karena mengandalkan panggilan butir-ke-bijian langsung. Untuk menghindari kebingungan, SMS dihapus, dan pengganti baru yang disebut Orleans.BroadcastChannel diperkenalkan.

BroadcastChannel hanya mendukung langganan implisit dan dapat menjadi pengganti langsung dalam kasus ini. Jika Anda memerlukan langganan eksplisit atau perlu menggunakan PersistentStream antarmuka (misalnya Anda menggunakan SMS dalam pengujian saat menggunakan EventHub dalam produksi), maka MemoryStream adalah kandidat terbaik untuk Anda.

BroadcastChannel akan memiliki perilaku yang sama dengan SMS, sementara MemoryStream akan berulah seperti penyedia streaming lainnya. Pertimbangkan contoh penggunaan Saluran Siaran berikut:

// Configuration
builder.AddBroadcastChannel(
    "my-provider",
    options => options.FireAndForgetDelivery = false);

// Publishing
var grainKey = Guid.NewGuid().ToString("N");
var channelId = ChannelId.Create("some-namespace", grainKey);
var stream = provider.GetChannelWriter<int>(channelId);

await stream.Publish(1);
await stream.Publish(2);
await stream.Publish(3);

// Simple implicit subscriber example
[ImplicitChannelSubscription]
public sealed class SimpleSubscriberGrain : Grain, ISubscriberGrain, IOnBroadcastChannelSubscribed
{
    // Called when a subscription is added to the grain
    public Task OnSubscribed(IBroadcastChannelSubscription streamSubscription)
    {
        streamSubscription.Attach<int>(
          item => OnPublished(streamSubscription.ChannelId, item),
          ex => OnError(streamSubscription.ChannelId, ex));

        return Task.CompletedTask;

        // Called when an item is published to the channel
        static Task OnPublished(ChannelId id, int item)
        {
            // Do something
            return Task.CompletedTask;
        }

        // Called when an error occurs
        static Task OnError(ChannelId id, Exception ex)
        {
            // Do something
            return Task.CompletedTask;
        }
    }
}

Migrasi ke MemoryStream akan lebih mudah, karena hanya konfigurasi yang perlu diubah. Pertimbangkan konfigurasi berikut MemoryStream :

builder.AddMemoryStreams<DefaultMemoryMessageBodySerializer>(
    "in-mem-provider",
    _ =>
    {
        // Number of pulling agent to start.
        // DO NOT CHANGE this value once deployed, if you do rolling deployment
        _.ConfigurePartitioning(partitionCount: 8);
    });

OpenTelemetry

Sistem telemetri telah diperbarui di Orleans 7.0 dan sistem sebelumnya telah dihapus demi API .NET standar seperti Metrik .NET untuk metrik dan ActivitySource untuk pelacakan.

Sebagai bagian dari ini, paket yang ada Microsoft.Orleans.TelemetryConsumers.* telah dihapus. Kami sedang mempertimbangkan untuk memperkenalkan serangkaian paket baru untuk menyederhanakan proses integrasi metrik yang dipancarkan oleh Orleans ke dalam solusi pemantauan pilihan Anda. Seperti biasa, umpan balik dan kontribusi dipersilakan.

Alat ini dotnet-counters menampilkan pemantauan performa untuk pemantauan kesehatan ad-hoc dan investigasi performa tingkat pertama. Untuk Orleans penghitung, alat penghitung dotnet dapat digunakan untuk memantaunya:

dotnet counters monitor -n MyApp --counters Microsoft.Orleans

Demikian pula, metrik OpenTelemetry dapat menambahkan Microsoft.Orleans meteran, seperti yang ditunjukkan dalam kode berikut:

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics => metrics
        .AddPrometheusExporter()
        .AddMeter("Microsoft.Orleans"));

Untuk mengaktifkan pelacakan terdistribusi, Anda mengonfigurasi OpenTelemetry seperti yang ditunjukkan dalam kode berikut:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.SetResourceBuilder(ResourceBuilder.CreateDefault()
            .AddService(serviceName: "ExampleService", serviceVersion: "1.0"));

        tracing.AddAspNetCoreInstrumentation();
        tracing.AddSource("Microsoft.Orleans.Runtime");
        tracing.AddSource("Microsoft.Orleans.Application");

        tracing.AddZipkinExporter(options =>
        {
            options.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
        });
    });

Dalam kode sebelumnya, OpenTelemetry dikonfigurasi untuk memantau:

  • Microsoft.Orleans.Runtime
  • Microsoft.Orleans.Application

Untuk menyebarluaskan aktivitas, panggil AddActivityPropagation:

builder.Host.UseOrleans((_, clientBuilder) =>
{
    clientBuilder.AddActivityPropagation();
});

Refaktor fitur dari paket inti ke dalam paket terpisah

Dalam Orleans 7.0, kami telah melakukan upaya untuk memperhitungkan ekstensi menjadi paket terpisah yang tidak bergantung pada Orleans.Core. Yaitu, , Orleans.StreamingOrleans.Reminders, dan Orleans.Transactions telah dipisahkan dari inti. Ini berarti bahwa paket ini sepenuhnya membayar untuk apa yang Anda gunakan dan tidak ada kode dalam inti Orleans yang didedikasikan untuk fitur-fitur ini. Ini menurunkan permukaan API inti dan ukuran perakitan, menyederhanakan inti, dan meningkatkan performa. Mengenai performa, Transaksi sebelumnya Orleans memerlukan beberapa kode yang dijalankan untuk setiap metode untuk mengoordinasikan potensi transaksi. Sejak itu dipindahkan ke per metode.

Ini adalah perubahan yang melanggar kompilasi. Anda mungkin memiliki kode yang ada yang berinteraksi dengan pengingat atau aliran dengan memanggil ke metode yang sebelumnya ditentukan pada Grain kelas dasar tetapi sekarang merupakan metode ekstensi. Panggilan seperti itu yang tidak menentukan this (misalnya GetReminders) perlu diperbarui untuk disertakan this (misalnya this.GetReminders()) karena metode ekstensi harus memenuhi syarat. Akan ada kesalahan kompilasi jika Anda tidak memperbarui panggilan tersebut dan perubahan kode yang diperlukan mungkin tidak jelas jika Anda tidak tahu apa yang telah berubah.

Klien transaksi

Orleans 7.0 memperkenalkan abstraksi baru untuk mengoordinasikan transaksi, Orleans.ITransactionClient. Sebelumnya, transaksi hanya dapat dikoordinasikan dengan biji-bijian. Dengan ITransactionClient, yang tersedia melalui injeksi dependensi, klien juga dapat mengoordinasikan transaksi tanpa memerlukan biji-bijian perantara. Contoh berikut menarik kredit dari satu akun dan menyetorkannya ke akun lain dalam satu transaksi. Kode ini dapat dipanggil dari dalam butir atau dari klien eksternal yang telah mengambil ITransactionClient dari kontainer injeksi dependensi.

await transactionClient.RunTransaction(
  TransactionOption.Create,
  () => Task.WhenAll(from.Withdraw(100), to.Deposit(100)));

Untuk transaksi yang dikoordinasikan oleh klien, klien harus menambahkan layanan yang diperlukan selama waktu konfigurasi:

clientBuilder.UseTransactions();

Sampel BankAccount menunjukkan penggunaan ITransactionClient. Untuk informasi selengkapnya, lihat Orleans transaksi.

Reentransi rantai panggilan

Biji-bijian berulir tunggal dan memproses permintaan satu per satu dari awal hingga selesai secara default. Dengan kata lain, biji-bijian tidak masuk kembali secara default. ReentrantAttribute Menambahkan ke kelas biji-bijian memungkinkan beberapa permintaan diproses secara bersamaan, secara interleaving, sambil tetap berulir tunggal. Ini dapat berguna untuk biji-bijian yang tidak memiliki status internal atau melakukan banyak operasi asinkron, seperti mengeluarkan panggilan HTTP atau menulis ke database. Perawatan ekstra perlu diambil ketika permintaan dapat diselingi: ada kemungkinan bahwa status biji-bijian diamati sebelum await pernyataan telah berubah pada saat operasi asinkron selesai dan metode melanjutkan eksekusi.

Misalnya, biji-bijian berikut mewakili penghitung. Ini telah ditandai Reentrant, memungkinkan beberapa panggilan untuk melakukan interleave. Metode Increment() harus menaikkan penghitung internal dan mengembalikan nilai yang diamati. Namun, karena Increment() tubuh metode mengamati status biji-bijian Increment() sebelum await titik dan memperbaruinya setelahnya, ada kemungkinan bahwa beberapa eksekusi interleaving dapat mengakibatkan _value kurang dari jumlah Increment() total panggilan yang diterima. Ini adalah kesalahan yang diperkenalkan oleh penggunaan reentrancy yang tidak tepat.

Menghapus ReentrantAttribute sudah cukup untuk memperbaiki masalah.

[Reentrant]
public sealed class CounterGrain : Grain, ICounterGrain
{
    int _value;
    
    /// <summary>
    /// Increments the grain's value and returns the previous value.
    /// </summary>
    public Task<int> Increment()
    {
        // Do not copy this code, it contains an error.
        var currentVal = _value;
        await Task.Delay(TimeSpan.FromMilliseconds(1_000));
        _value = currentVal + 1;
        return currentValue;
    }
}

Untuk mencegah kesalahan tersebut, biji-bijian tidak bersifat reentrant secara default. Kelemahan dari ini adalah berkurangnya throughput untuk biji-bijian yang melakukan operasi asinkron dalam implementasinya, karena permintaan lain tidak dapat diproses saat biji-bijian menunggu operasi asinkron selesai. Untuk meringankan ini, Orleans menawarkan beberapa opsi untuk memungkinkan masuknya kembali dalam kasus tertentu:

  • Untuk seluruh kelas: menempatkan ReentrantAttribute pada butir memungkinkan permintaan apa pun ke biji-bijian untuk bersinggungan dengan permintaan lain.
  • Untuk subset metode: menempatkan pada metode antarmuka biji-bijian AlwaysInterleaveAttribute memungkinkan permintaan ke metode tersebut untuk berinteraksi dengan permintaan lain dan meminta permintaan ke metode tersebut untuk diselingi oleh permintaan lain.
  • Untuk subset metode: menempatkan pada metode antarmuka biji-bijian ReadOnlyAttribute memungkinkan permintaan ke metode tersebut untuk berinteraksi dengan permintaan lain ReadOnly dan meminta permintaan ke metode tersebut untuk diselingi oleh permintaan lainReadOnly. Dalam pengertian ini, ini adalah bentuk yang lebih terbatas dari AlwaysInterleave.
  • Untuk permintaan apa pun dalam rantai panggilan: RequestContext.AllowCallChainReentrancy() dan <xref:Orleans. Runtime.RequestContext.SuppressCallChainReentrancy?displayProperty=nameWithType memungkinkan memilih masuk dan keluar dari mengizinkan permintaan hilir untuk masuk kembali ke grain. Panggilan mengembalikan nilai yang harus dibuang saat keluar dari permintaan. Oleh karena itu, penggunaan yang tepat adalah sebagai berikut:
public Task<int> OuterCall(IMyGrain other)
{
    // Allow call-chain reentrancy for this grain, for the duration of the method.
    using var _ = RequestContext.AllowCallChainReentrancy();
    await other.CallMeBack(this.AsReference<IMyGrain>());
}

public Task CallMeBack(IMyGrain grain)
{
    // Because OuterCall allowed reentrancy back into that grain, this method 
    // will be able to call grain.InnerCall() without deadlocking.
    await grain.InnerCall();
}

public Task InnerCall() => Task.CompletedTask;

Reentransi rantai panggilan harus diikutsertakan per butir, per rantai panggilan. Misalnya, pertimbangkan dua butir, biji-bijian A & biji-bijian B. Jika grain A memungkinkan reentransi rantai panggilan sebelum memanggil biji-bijian B, biji-bijian B dapat memanggil kembali ke butir A dalam panggilan itu. Namun, biji-bijian A tidak dapat memanggil kembali ke biji-bijian B jika biji-bijian B belum juga mengaktifkan reentransi rantai panggilan. Ini adalah per-butir, per-call-chain.

Biji-bijian juga dapat menekan informasi masuknya kembali rantai panggilan dari mengalir ke bawah rantai panggilan menggunakan using var _ = RequestContext.SuppressCallChainReentrancy(). Ini mencegah panggilan berikutnya masuk kembali.

Skrip migrasi ADO.NET

Untuk memastikan kompatibilitas penerusan dengan Orleans pengklusteran, persistensi, dan pengingat yang mengandalkan ADO.NET, Anda memerlukan skrip migrasi SQL yang sesuai:

Pilih file untuk database yang digunakan dan terapkan secara berurutan.