Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Secara luas ada dua jenis serialisasi yang digunakan dalam Orleans:
- Serialisasi panggilan biji-bijian: Digunakan untuk membuat serialisasi objek yang diteruskan ke dan dari biji-bijian.
- Serialisasi penyimpanan biji-bijian: Digunakan untuk menserialisasikan objek ke dan dari sistem penyimpanan.
Sebagian besar artikel ini berfokus pada serialisasi panggilan grain melalui kerangka kerja serialisasi yang disertakan dalam Orleans. Bagian Serializer penyimpanan grain membahas serialisasi penyimpanan grain.
Gunakan Orleans serialisasi
Orleans mencakup kerangka kerja serialisasi tingkat lanjut dan dapat diperluas yang disebut sebagai Orleans. Serialisasi. Kerangka kerja serialisasi yang disertakan dalam Orleans dirancang untuk memenuhi tujuan berikut:
- Performa tinggi: Serializer dirancang dan dioptimalkan untuk performa. Detail selengkapnya tersedia dalam presentasi ini.
- Presisi tinggi: Serializer mewakili dengan setia sebagian besar sistem tipe .NET, termasuk dukungan untuk generik, polimorfisme, hierarki pewarisan, identitas objek, dan grafik siklik. Pointer tidak didukung karena tidak portabel di seluruh proses.
- Fleksibilitas: Anda dapat menyesuaikan serializer untuk mendukung pustaka pihak ketiga dengan membuat pengganti atau mendelegasikan ke pustaka serialisasi eksternal seperti System.Text.Json, Newtonsoft.Json, MessagePack, dan Google.Protobuf.
-
Toleransi versi: Serializer memungkinkan jenis aplikasi berevolusi dari waktu ke waktu, mendukung:
- Menambahkan dan menghapus anggota
- Penurunan Kelas
- Pelesiran dan penyempitan numerik (misalnya,
intke/darilong,floatke/daridouble) - Jenis penggantian nama
Representasi dengan ketelitian tinggi dari tipe cukup jarang ditemukan pada serializer, sehingga beberapa poin memerlukan penjelasan lebih lanjut.
Jenis dinamis dan polimorfisme arbitrer: Orleans tidak memberlakukan pembatasan pada jenis yang diteruskan dalam panggilan biji-bijian dan mempertahankan sifat dinamis dari jenis data aktual. Ini berarti, misalnya, jika suatu metode dalam antarmuka grain dinyatakan menerima IDictionary, tetapi pada runtime pengirim mengirimkan SortedDictionary<TKey,TValue>, penerima mendapatkan
SortedDictionary(meskipun antarmuka "kontrak statis"/grain tidak menentukan perilaku ini).Mempertahankan identitas objek: Jika objek yang sama diteruskan beberapa kali dalam argumen panggilan grain atau ditunjuk lebih dari sekali secara tidak langsung dari argumen, Orleans mengserialisasikannya hanya satu kali. Di sisi penerima, Orleans memulihkan semua referensi dengan benar sehingga dua pointer ke objek yang sama masih menunjuk ke objek yang sama setelah deserialisasi. Mempertahankan identitas objek penting dalam skenario seperti berikut: Bayangkan grain A mengirim kamus dengan 100 entri ke grain B, dan 10 kunci di kamus menunjuk ke objek yang sama,
obj, di sisi A. Tanpa mempertahankan identitas objek, B akan menerima kamus 100 entri dengan 10 kunci yang menunjuk ke 10 klon yang berbeda dariobj. Dengan identitas objek yang dipertahankan, kamus di sisi B terlihat persis seperti di sisi A, dengan 10 kunci tersebut menunjuk ke satu objekobj. Perhatikan bahwa karena implementasi kode hash string default di .NET diacak per proses, urutan nilai dalam kamus dan set hash (misalnya) mungkin tidak dipertahankan.
Untuk mendukung toleransi versi, penyusun mengharuskan Anda untuk menyatakan dengan jelas jenis dan anggota mana yang akan diserialisasikan. Kami telah mencoba untuk membuat ini semudah mungkin. Tandai semua jenis yang dapat diserialisasikan dengan Orleans.GenerateSerializerAttribute untuk menginstruksikan Orleans untuk menghasilkan kode serializer untuk jenis Anda. Setelah melakukan ini, Anda dapat menggunakan perbaikan kode yang disertakan untuk menambahkan yang diperlukan Orleans.IdAttribute ke anggota yang dapat diserialisasikan pada jenis Anda, seperti yang ditunjukkan di sini:
Berikut adalah contoh jenis yang dapat diserialisasikan dalam Orleans, yang menunjukkan cara menerapkan atribut.
[GenerateSerializer]
public class Employee
{
[Id(0)]
public string Name { get; set; }
}
Orleans mendukung pewarisan dan menserialisasikan lapisan individu dalam hierarki secara terpisah, memungkinkan mereka memiliki ID anggota yang berbeda.
[GenerateSerializer]
public class Publication
{
[Id(0)]
public string Title { get; set; }
}
[GenerateSerializer]
public class Book : Publication
{
[Id(0)]
public string ISBN { get; set; }
}
Dalam kode sebelumnya, perhatikan bahwa baik Publication maupun Book memiliki anggota dengan [Id(0)], meskipun Book berasal dari Publication. Ini adalah praktik yang direkomendasikan di Orleans karena pengidentifikasi anggota dilingkupi ke tingkat pewarisan, bukan tipe secara keseluruhan. Anda dapat menambahkan dan menghapus anggota dari Publication dan Book secara independen, tetapi Anda tidak dapat memasukkan kelas dasar baru ke dalam hierarki setelah aplikasi disebarkan tanpa pertimbangan khusus.
Orleans juga mendukung jenis serialisasi dengan internal, private, dan readonly anggota, seperti pada jenis contoh ini.
[GenerateSerializer]
public struct MyCustomStruct
{
public MyCustomStruct(int intProperty, int intField)
{
IntProperty = intProperty;
_intField = intField;
}
[Id(0)]
public int IntProperty { get; }
[Id(1)] private readonly int _intField;
public int GetIntField() => _intField;
public override string ToString() => $"{nameof(_intField)}: {_intField}, {nameof(IntProperty)}: {IntProperty}";
}
Secara bawaan, Orleans menyerialkan tipe Anda dengan mengodekan nama lengkapnya. Anda dapat mengambil alih ini dengan menambahkan Orleans.AliasAttribute. Melakukannya mengakibatkan tipe Anda diserialisasikan menggunakan nama yang tahan terhadap penggantian nama kelas yang mendasar atau pemindahan di antara perakitan. Alias jenis dilingkup secara global, dan Anda tidak dapat memiliki dua alias dengan nilai yang sama dalam aplikasi. Untuk tipe generik, nilai alias harus mencakup jumlah parameter generik yang diikuti oleh tanda backtick; misalnya, MyGenericType<T, U> dapat memiliki alias [Alias("mytype`2")].
Jenis serialisasi record
Anggota yang ditentukan dalam konstruktor utama rekaman memiliki ID implisit secara default. Dengan kata lain, Orleans mendukung jenis serialisasi record . Ini berarti Anda tidak dapat mengubah urutan parameter untuk jenis yang sudah disebarkan, karena itu merusak kompatibilitas dengan versi aplikasi Anda sebelumnya (dalam skenario peningkatan bergulir) dan dengan instans serial dari jenis tersebut dalam penyimpanan dan aliran. Anggota yang ditentukan dalam isi jenis catatan tidak berbagi identitas dengan parameter konstruktor utama.
[GenerateSerializer]
public record MyRecord(string A, string B)
{
// ID 0 won't clash with A in primary constructor as they don't share identities
[Id(0)]
public string C { get; init; }
}
Jika Anda tidak ingin parameter konstruktor utama secara otomatis disertakan sebagai bidang yang dapat diserialisasikan, gunakan [GenerateSerializer(IncludePrimaryConstructorParameters = false)].
Serialisasi MessagePack
Dimulai dengan Orleans 8.2, Anda dapat menggunakan MessagePack sebagai serializer eksternal untuk Orleans. MessagePack adalah format serialisasi biner berkinerja tinggi yang menghasilkan payload yang lebih kecil daripada JSON sambil mempertahankan kecepatan serialisasi dan deserialisasi yang cepat.
Kapan menggunakan MessagePack
Pertimbangkan untuk menggunakan MessagePack saat:
- Anda memerlukan interoperabilitas dengan sistem non-.NET yang mendukung MessagePack
- Anda memiliki jenis yang sudah dianotasi dengan atribut MessagePack (
[MessagePackObject],[Key]) - Anda ingin ukuran payload yang lebih kecil dibandingkan dengan serializer berbasis JSON
- Anda memerlukan format biner standar
Untuk sebagian besar Orleans aplikasi, serializer default Orleans direkomendasikan karena memberikan keakuratan yang lebih tinggi (dukungan sistem jenis.NET), pelestarian identitas objek, dan pembuatan serializer otomatis.
Menginstal paket MessagePack serializer
Tambahkan paket serializer MessagePack ke proyek Anda:
dotnet add package Microsoft.Orleans.Serialization.MessagePack
Mengonfigurasi serialisasi MessagePack
Untuk mengonfigurasi serialisasi MessagePack, gunakan AddMessagePackSerializer metode ekstensi:
public static void ConfigureMessagePackBasic(string[] args)
{
var builder = Host.CreateApplicationBuilder(args);
builder.UseOrleans(siloBuilder =>
{
siloBuilder.UseLocalhostClustering();
siloBuilder.Services.AddSerializer(serializerBuilder => serializerBuilder.AddMessagePackSerializer(
isSerializable: type => type.Namespace?.StartsWith("MyApp.Messages") == true,
isCopyable: type => false,
messagePackSerializerOptions: null
));
});
}
Delegasi isSerializable mengontrol jenis mana yang ditangani oleh serializer MessagePack. Jenis yang tidak sesuai dengan predikat ini akan menggunakan serializer Orleans default.
MessagePackCodecOptions
Anda dapat mengonfigurasi serializer MessagePack menggunakan MessagePackCodecOptions:
| Harta benda | Tipe | Bawaan | Description |
|---|---|---|---|
SerializerOptions |
MessagePackSerializerOptions |
MessagePackSerializerOptions.Standard |
Opsi serializer MessagePack yang digunakan untuk serialisasi. |
AllowDataContractAttributes |
bool |
false |
Ketika true, memungkinkan jenis yang ditandai dengan [DataContract] untuk diserialisasikan menggunakan MessagePack. |
IsSerializableType |
Func<Type, bool?> |
null |
Delegasi untuk menentukan apakah suatu tipe harus di-serialisasi oleh MessagePack. |
IsCopyableType |
Func<Type, bool?> |
null |
Delegasi untuk menentukan apakah tipe data harus disalin oleh MessagePack. |
Contoh: Mengonfigurasi dengan opsi
public static void ConfigureMessagePackWithOptions(string[] args)
{
var builder = Host.CreateApplicationBuilder(args);
builder.UseOrleans(siloBuilder =>
{
siloBuilder.UseLocalhostClustering();
siloBuilder.Services.AddSerializer(serializerBuilder => serializerBuilder.AddMessagePackSerializer(
isSerializable: type => type.GetCustomAttribute<MessagePackObjectAttribute>() != null,
isCopyable: type => false,
configureOptions: options => options.Configure(opts =>
{
opts.SerializerOptions = MessagePackSerializerOptions.Standard
.WithCompression(MessagePackCompression.Lz4BlockArray);
opts.AllowDataContractAttributes = true;
})
));
});
}
Menentukan jenis MessagePack
Jenis yang diserialisasikan oleh MessagePack harus menggunakan atribut MessagePack:
[MessagePackObject]
public class OrderMessage
{
[Key(0)]
public string OrderId { get; set; }
[Key(1)]
public decimal Amount { get; set; }
[Key(2)]
public DateTime CreatedAt { get; set; }
[Key(3)]
public List<string> Items { get; set; }
}
Anda kemudian dapat menggunakan jenis ini dalam antarmuka grain:
public interface IOrderGrain : IGrainWithStringKey
{
Task<OrderMessage> GetOrder();
Task PlaceOrder(OrderMessage order);
}
Perbandingan Serializer
| Fitur | Orleans Natif | MessagePack | System.Text.Json |
|---|---|---|---|
| Rancangan | Binary | Binary | Teks (JSON) |
| Kesetiaan tipe .NET | Baik Sekali | Baik | Terbatas |
| Identitas objek | Yes | Tidak. | Tidak. |
| Ukuran muatan | Kecil | Terkecil | Terbesar |
| Lintas platform | .NET saja | Klien MessagePack apa pun | Klien JSON apa pun |
| Toleransi versi | Yes | Yes | Yes |
| Penyetelan diperlukan | None | Atribut eksplisit | Atribut eksplisit |
Pengganti untuk membuat serial tipe eksternal
Terkadang, Anda mungkin perlu meneruskan jenis antara biji-bijian di mana Anda tidak memiliki kontrol penuh. Dalam kasus ini, mengonversi secara manual ke dan dari jenis yang ditentukan khusus dalam kode aplikasi Anda mungkin tidak praktis. Orleans menawarkan solusi untuk situasi ini: tipe pengganti. Surrogat diserialisasikan menggantikan jenis target dan memiliki fungsionalitas untuk mengonversi dari dan ke jenis target tersebut. Pertimbangkan contoh berikut dari tipe asing serta pengganti dan konverter yang sesuai:
// This is the foreign type, which you do not have control over.
public struct MyForeignLibraryValueType
{
public MyForeignLibraryValueType(int num, string str, DateTimeOffset dto)
{
Num = num;
String = str;
DateTimeOffset = dto;
}
public int Num { get; }
public string String { get; }
public DateTimeOffset DateTimeOffset { get; }
}
// This is the surrogate which will act as a stand-in for the foreign type.
// Surrogates should use plain fields instead of properties for better performance.
[GenerateSerializer]
public struct MyForeignLibraryValueTypeSurrogate
{
[Id(0)]
public int Num;
[Id(1)]
public string String;
[Id(2)]
public DateTimeOffset DateTimeOffset;
}
// This is a converter that converts between the surrogate and the foreign type.
[RegisterConverter]
public sealed class MyForeignLibraryValueTypeSurrogateConverter :
IConverter<MyForeignLibraryValueType, MyForeignLibraryValueTypeSurrogate>
{
public MyForeignLibraryValueType ConvertFromSurrogate(
in MyForeignLibraryValueTypeSurrogate surrogate) =>
new(surrogate.Num, surrogate.String, surrogate.DateTimeOffset);
public MyForeignLibraryValueTypeSurrogate ConvertToSurrogate(
in MyForeignLibraryValueType value) =>
new()
{
Num = value.Num,
String = value.String,
DateTimeOffset = value.DateTimeOffset
};
}
Dalam kode sebelumnya:
-
MyForeignLibraryValueTypeadalah tipe di luar kendali Anda, yang didefinisikan dalam pustaka yang digunakan. -
MyForeignLibraryValueTypeSurrogateadalah pemetaan jenis pengganti keMyForeignLibraryValueType. -
RegisterConverterAttribute menentukan bahwa
MyForeignLibraryValueTypeSurrogateConverterbertindak sebagai pengonversi untuk memetakan antara dua jenis. Kelas mengimplementasikan IConverter<TValue,TSurrogate> antarmuka.
Orleans mendukung serialisasi jenis dalam hierarki jenis (jenis yang berasal dari jenis lain). Jika tipe asing mungkin muncul dalam hierarki tipe (misalnya, sebagai kelas dasar untuk salah satu tipe Anda sendiri), Anda juga harus mengimplementasikan antarmuka Orleans.IPopulator<TValue,TSurrogate>. Pertimbangkan contoh berikut:
// The foreign type is not sealed, allowing other types to inherit from it.
public class MyForeignLibraryType
{
public MyForeignLibraryType() { }
public MyForeignLibraryType(int num, string str, DateTimeOffset dto)
{
Num = num;
String = str;
DateTimeOffset = dto;
}
public int Num { get; set; }
public string String { get; set; }
public DateTimeOffset DateTimeOffset { get; set; }
}
// The surrogate is defined as it was in the previous example.
[GenerateSerializer]
public struct MyForeignLibraryTypeSurrogate
{
[Id(0)]
public int Num;
[Id(1)]
public string String;
[Id(2)]
public DateTimeOffset DateTimeOffset;
}
// Implement the IConverter and IPopulator interfaces on the converter.
[RegisterConverter]
public sealed class MyForeignLibraryTypeSurrogateConverter :
IConverter<MyForeignLibraryType, MyForeignLibraryTypeSurrogate>,
IPopulator<MyForeignLibraryType, MyForeignLibraryTypeSurrogate>
{
public MyForeignLibraryType ConvertFromSurrogate(
in MyForeignLibraryTypeSurrogate surrogate) =>
new(surrogate.Num, surrogate.String, surrogate.DateTimeOffset);
public MyForeignLibraryTypeSurrogate ConvertToSurrogate(
in MyForeignLibraryType value) =>
new()
{
Num = value.Num,
String = value.String,
DateTimeOffset = value.DateTimeOffset
};
public void Populate(
in MyForeignLibraryTypeSurrogate surrogate, MyForeignLibraryType value)
{
value.Num = surrogate.Num;
value.String = surrogate.String;
value.DateTimeOffset = surrogate.DateTimeOffset;
}
}
// Application types can inherit from the foreign type, assuming they're not sealed
// since Orleans knows how to serialize it.
[GenerateSerializer]
public sealed class DerivedFromMyForeignLibraryType : MyForeignLibraryType
{
public DerivedFromMyForeignLibraryType() { }
public DerivedFromMyForeignLibraryType(
int intValue, int num, string str, DateTimeOffset dto) : base(num, str, dto)
{
IntValue = intValue;
}
[Id(0)]
public int IntValue { get; set; }
}
Aturan pengelolaan versi
Toleransi versi didukung asalkan Anda mengikuti sekumpulan aturan saat memodifikasi jenis. Jika Anda terbiasa dengan sistem seperti Google Protocol Buffers (Protobuf), aturan ini akan akrab.
Jenis campuran (class & struct)
- Pewarisan didukung, tetapi memodifikasi hierarki warisan objek tidak didukung. Anda tidak dapat menambahkan, mengubah, atau menghapus kelas dasar kelas.
- Dengan pengecualian beberapa jenis numerik yang dijelaskan di bagian Numerik di bawah ini, Anda tidak dapat mengubah jenis bidang.
- Anda dapat menambahkan atau menghapus bidang kapan saja dalam hierarki pewarisan.
- Anda tidak dapat mengubah ID bidang.
- ID bidang harus unik untuk setiap tingkat dalam hierarki jenis tetapi dapat digunakan kembali antara kelas dasar dan subkelas. Misalnya,
Basekelas dapat mendeklarasikan bidang dengan ID0, danSub : Basekelas dapat mendeklarasikan bidang yang berbeda dengan ID yang sama,0.
Numerik
- Anda tidak dapat mengubah tanda tangan bidang numerik.
- Konversi antara
int&uinttidak valid.
- Konversi antara
- Anda dapat mengubah lebar bidang numerik.
- Misalnya, konversi dari
intkelongatauulongkeushortdidukung. - Konversi yang mempersempit rentang melemparkan pengecualian jika nilai waktu proses bidang menyebabkan overflow.
- Konversi dari
ulongkeushorthanya didukung jika nilai runtime kurang dariushort.MaxValue. - Konversi dari
doublekefloathanya didukung jika nilai runtime antarafloat.MinValuedanfloat.MaxValue. - Demikian pula untuk
decimal, yang memiliki rentang yang lebih sempit daripada dandoublefloat.
- Misalnya, konversi dari
Mesin Fotokopi
Orleans mempromosikan keamanan secara bawaan, termasuk keamanan dari beberapa jenis bug konkurensi. Secara khusus, Orleans segera menyalin objek yang diteruskan dalam panggilan grain secara bawaan. Orleans. Serialisasi memfasilitasi penyalinan ini. Saat Anda menerapkan Orleans.CodeGeneration.GenerateSerializerAttribute ke jenis, Orleans juga menghasilkan salinan untuk jenis tersebut. Orleans menghindari penyalinan jenis atau anggota individu yang ditandai dengan ImmutableAttribute. Untuk detail selengkapnya, lihat Serialisasi jenis yang tidak dapat diubah di Orleans.
Praktik terbaik serialisasi
✅ Berikan alias untuk jenis Anda dengan menggunakan atribut
[Alias("my-type")]. Jenis dengan alias dapat diganti namanya tanpa melanggar kompatibilitas.❌ Jangan mengubah
recordmenjadiclassyang biasa atau sebaliknya. Rekaman dan kelas tidak diwakili secara identik karena rekaman memiliki anggota konstruktor utama selain anggota reguler; oleh karena itu, keduanya tidak dapat dipertukarkan.❌ Jangan tambahkan tipe baru ke hierarki tipe yang sudah ada untuk jenis yang dapat diserialisasikan. Anda tidak boleh menambahkan kelas dasar baru ke tipe yang sudah ada. Anda dapat menambahkan subkelas baru dengan aman ke jenis yang sudah ada.
✅ Ganti penggunaan SerializableAttribute dengan GenerateSerializerAttribute dan deklarasi IdAttribute yang sesuai.
✅ Mulailah semua ID anggota dari nol untuk setiap jenis. ID dalam subkelas dan kelas dasarnya dapat tumpang tindih dengan aman. Kedua properti dalam contoh berikut memiliki ID yang sama dengan
0.[GenerateSerializer] public class MyBaseClass { [Id(0)] public int MyBaseInt { get; set; } } [GenerateSerializer] public sealed class MySubClass : MyBaseClass { [Id(0)] public int MySubInt { get; set; } }✅ Lakukan perlebar jenis anggota numerik sesuai kebutuhan. Anda dapat memperlebar
sbytekeshortkeintkelong.- Anda dapat mempersempit jenis anggota numerik, tetapi menghasilkan pengecualian runtime jika nilai yang diamati tidak dapat diwakili dengan benar oleh jenis yang dipersempit. Misalnya,
int.MaxValuetidak dapat diwakili olehshortbidang, sehingga mempersempitintbidang untukshortdapat menghasilkan pengecualian runtime jika nilai seperti itu ditemui.
- Anda dapat mempersempit jenis anggota numerik, tetapi menghasilkan pengecualian runtime jika nilai yang diamati tidak dapat diwakili dengan benar oleh jenis yang dipersempit. Misalnya,
❌ Jangan mengubah sifat bertanda dari elemen tipe numerik. Anda tidak boleh mengubah jenis anggota dari
uintkeintatauintkeuint, misalnya.
Pengatur urutan penyimpanan biji-bijian
Orleans termasuk model persistensi yang didukung penyedia untuk grain, dapat diakses melalui properti State atau dengan menyuntikkan satu atau beberapa nilai IPersistentState<TState> ke dalam grain Anda. Sebelum Orleans 7.0, setiap penyedia memiliki mekanisme yang berbeda untuk mengonfigurasi serialisasi. Dalam Orleans 7.0, sekarang ada antarmuka serializer status butir tujuan umum, IGrainStorageSerializer, menawarkan cara yang konsisten untuk menyesuaikan serialisasi status untuk setiap penyedia. Penyedia penyimpanan yang didukung menerapkan pola yang melibatkan pengaturan IStorageProviderSerializerOptions.GrainStorageSerializer properti pada kelas opsi penyedia, misalnya:
- DynamoDBStorageOptions.GrainStorageSerializer
- AzureBlobStorageOptions.GrainStorageSerializer
- AzureTableStorageOptions.GrainStorageSerializer
- GrainStorageSerializer
Serialisasi penyimpanan grain saat ini default untuk Newtonsoft.Json membuat serialisasi status. Anda dapat mengganti ini dengan memodifikasi properti tersebut pada waktu konfigurasi. Contoh berikut menunjukkan ini menggunakan OptionsBuilder<TOptions>:
public static void ConfigureGrainStorageSerializer(ISiloBuilder siloBuilder)
{
siloBuilder.AddAzureBlobGrainStorage(
"MyGrainStorage",
(OptionsBuilder<AzureBlobStorageOptions> optionsBuilder) =>
{
optionsBuilder.Configure<MyCustomSerializer>(
(options, serializer) => options.GrainStorageSerializer = serializer);
});
}
Untuk informasi selengkapnya, lihat OptionsBuilder API.
Secara luas ada dua jenis serialisasi yang digunakan dalam Orleans:
- Serialisasi panggilan biji-bijian: Digunakan untuk membuat serialisasi objek yang diteruskan ke dan dari biji-bijian.
- Serialisasi penyimpanan biji-bijian: Digunakan untuk menserialisasikan objek ke dan dari sistem penyimpanan.
Sebagian besar artikel ini berfokus pada serialisasi panggilan grain melalui kerangka kerja serialisasi yang disertakan dalam Orleans. Bagian Serializer penyimpanan grain membahas serialisasi penyimpanan grain.
Gunakan Orleans serialisasi
Orleans mencakup kerangka kerja serialisasi tingkat lanjut dan dapat diperluas yang disebut sebagai Orleans. Serialisasi. Kerangka kerja serialisasi yang disertakan dalam Orleans dirancang untuk memenuhi tujuan berikut:
- Performa tinggi: Serializer dirancang dan dioptimalkan untuk performa. Detail selengkapnya tersedia dalam presentasi ini.
- Presisi tinggi: Serializer mewakili dengan setia sebagian besar sistem tipe .NET, termasuk dukungan untuk generik, polimorfisme, hierarki pewarisan, identitas objek, dan grafik siklik. Pointer tidak didukung karena tidak portabel di seluruh proses.
- Fleksibilitas: Anda dapat menyesuaikan serializer untuk mendukung pustaka pihak ketiga dengan membuat pengganti atau mendelegasikan ke pustaka serialisasi eksternal seperti System.Text.Json, Newtonsoft.Json, dan Google.Protobuf.
-
Toleransi versi: Serializer memungkinkan jenis aplikasi berevolusi dari waktu ke waktu, mendukung:
- Menambahkan dan menghapus anggota
- Penurunan Kelas
- Pelesiran dan penyempitan numerik (misalnya,
intke/darilong,floatke/daridouble) - Jenis penggantian nama
Representasi dengan ketelitian tinggi dari tipe cukup jarang ditemukan pada serializer, sehingga beberapa poin memerlukan penjelasan lebih lanjut.
Jenis dinamis dan polimorfisme arbitrer: Orleans tidak memberlakukan pembatasan pada jenis yang diteruskan dalam panggilan biji-bijian dan mempertahankan sifat dinamis dari jenis data aktual. Ini berarti, misalnya, jika suatu metode dalam antarmuka grain dinyatakan menerima IDictionary, tetapi pada runtime pengirim mengirimkan SortedDictionary<TKey,TValue>, penerima mendapatkan
SortedDictionary(meskipun antarmuka "kontrak statis"/grain tidak menentukan perilaku ini).Mempertahankan identitas objek: Jika objek yang sama diteruskan beberapa kali dalam argumen panggilan grain atau ditunjuk lebih dari sekali secara tidak langsung dari argumen, Orleans mengserialisasikannya hanya satu kali. Di sisi penerima, Orleans memulihkan semua referensi dengan benar sehingga dua pointer ke objek yang sama masih menunjuk ke objek yang sama setelah deserialisasi. Mempertahankan identitas objek penting dalam skenario seperti berikut: Bayangkan grain A mengirim kamus dengan 100 entri ke grain B, dan 10 kunci di kamus menunjuk ke objek yang sama,
obj, di sisi A. Tanpa mempertahankan identitas objek, B akan menerima kamus 100 entri dengan 10 kunci yang menunjuk ke 10 klon yang berbeda dariobj. Dengan identitas objek yang dipertahankan, kamus di sisi B terlihat persis seperti di sisi A, dengan 10 kunci tersebut menunjuk ke satu objekobj. Perhatikan bahwa karena implementasi kode hash string default di .NET diacak per proses, urutan nilai dalam kamus dan set hash (misalnya) mungkin tidak dipertahankan.
Untuk mendukung toleransi versi, penyusun mengharuskan Anda untuk menyatakan dengan jelas jenis dan anggota mana yang akan diserialisasikan. Kami telah mencoba untuk membuat ini semudah mungkin. Tandai semua jenis yang dapat diserialisasikan dengan Orleans.GenerateSerializerAttribute untuk menginstruksikan Orleans untuk menghasilkan kode serializer untuk jenis Anda. Setelah melakukan ini, Anda dapat menggunakan perbaikan kode yang disertakan untuk menambahkan yang diperlukan Orleans.IdAttribute ke anggota yang dapat diserialisasikan pada jenis Anda, seperti yang ditunjukkan di sini:
Berikut adalah contoh jenis yang dapat diserialisasikan dalam Orleans, yang menunjukkan cara menerapkan atribut.
[GenerateSerializer]
public class Employee
{
[Id(0)]
public string Name { get; set; }
}
Orleans mendukung pewarisan dan menserialisasikan lapisan individu dalam hierarki secara terpisah, memungkinkan mereka memiliki ID anggota yang berbeda.
[GenerateSerializer]
public class Publication
{
[Id(0)]
public string Title { get; set; }
}
[GenerateSerializer]
public class Book : Publication
{
[Id(0)]
public string ISBN { get; set; }
}
Dalam kode sebelumnya, perhatikan bahwa baik Publication maupun Book memiliki anggota dengan [Id(0)], meskipun Book berasal dari Publication. Ini adalah praktik yang direkomendasikan di Orleans karena pengidentifikasi anggota dilingkupi ke tingkat pewarisan, bukan tipe secara keseluruhan. Anda dapat menambahkan dan menghapus anggota dari Publication dan Book secara independen, tetapi Anda tidak dapat memasukkan kelas dasar baru ke dalam hierarki setelah aplikasi disebarkan tanpa pertimbangan khusus.
Orleans juga mendukung jenis serialisasi dengan internal, private, dan readonly anggota, seperti pada jenis contoh ini.
[GenerateSerializer]
public struct MyCustomStruct
{
public MyCustom(int intProperty, int intField)
{
IntProperty = intProperty;
_intField = intField;
}
[Id(0)]
public int IntProperty { get; }
[Id(1)] private readonly int _intField;
public int GetIntField() => _intField;
public override string ToString() => $"{nameof(_intField)}: {_intField}, {nameof(IntProperty)}: {IntProperty}";
}
Secara bawaan, Orleans menyerialkan tipe Anda dengan mengodekan nama lengkapnya. Anda dapat mengambil alih ini dengan menambahkan Orleans.AliasAttribute. Melakukannya mengakibatkan tipe Anda diserialisasikan menggunakan nama yang tahan terhadap penggantian nama kelas yang mendasar atau pemindahan di antara perakitan. Alias jenis dilingkup secara global, dan Anda tidak dapat memiliki dua alias dengan nilai yang sama dalam aplikasi. Untuk tipe generik, nilai alias harus mencakup jumlah parameter generik yang diikuti oleh tanda backtick; misalnya, MyGenericType<T, U> dapat memiliki alias [Alias("mytype`2")].
Jenis serialisasi record
Anggota yang ditentukan dalam konstruktor utama rekaman memiliki ID implisit secara default. Dengan kata lain, Orleans mendukung jenis serialisasi record . Ini berarti Anda tidak dapat mengubah urutan parameter untuk jenis yang sudah disebarkan, karena itu merusak kompatibilitas dengan versi aplikasi Anda sebelumnya (dalam skenario peningkatan bergulir) dan dengan instans serial dari jenis tersebut dalam penyimpanan dan aliran. Anggota yang ditentukan dalam isi jenis catatan tidak berbagi identitas dengan parameter konstruktor utama.
[GenerateSerializer]
public record MyRecord(string A, string B)
{
// ID 0 won't clash with A in primary constructor as they don't share identities
[Id(0)]
public string C { get; init; }
}
Jika Anda tidak ingin parameter konstruktor utama secara otomatis disertakan sebagai bidang yang dapat diserialisasikan, gunakan [GenerateSerializer(IncludePrimaryConstructorParameters = false)].
Pengganti untuk membuat serial tipe eksternal
Terkadang, Anda mungkin perlu meneruskan jenis antara biji-bijian di mana Anda tidak memiliki kontrol penuh. Dalam kasus ini, mengonversi secara manual ke dan dari jenis yang ditentukan khusus dalam kode aplikasi Anda mungkin tidak praktis. Orleans menawarkan solusi untuk situasi ini: tipe pengganti. Surrogat diserialisasikan menggantikan jenis target dan memiliki fungsionalitas untuk mengonversi dari dan ke jenis target tersebut. Pertimbangkan contoh berikut dari tipe asing serta pengganti dan konverter yang sesuai:
// This is the foreign type, which you do not have control over.
public struct MyForeignLibraryValueType
{
public MyForeignLibraryValueType(int num, string str, DateTimeOffset dto)
{
Num = num;
String = str;
DateTimeOffset = dto;
}
public int Num { get; }
public string String { get; }
public DateTimeOffset DateTimeOffset { get; }
}
// This is the surrogate which will act as a stand-in for the foreign type.
// Surrogates should use plain fields instead of properties for better performance.
[GenerateSerializer]
public struct MyForeignLibraryValueTypeSurrogate
{
[Id(0)]
public int Num;
[Id(1)]
public string String;
[Id(2)]
public DateTimeOffset DateTimeOffset;
}
// This is a converter that converts between the surrogate and the foreign type.
[RegisterConverter]
public sealed class MyForeignLibraryValueTypeSurrogateConverter :
IConverter<MyForeignLibraryValueType, MyForeignLibraryValueTypeSurrogate>
{
public MyForeignLibraryValueType ConvertFromSurrogate(
in MyForeignLibraryValueTypeSurrogate surrogate) =>
new(surrogate.Num, surrogate.String, surrogate.DateTimeOffset);
public MyForeignLibraryValueTypeSurrogate ConvertToSurrogate(
in MyForeignLibraryValueType value) =>
new()
{
Num = value.Num,
String = value.String,
DateTimeOffset = value.DateTimeOffset
};
}
Dalam kode sebelumnya:
-
MyForeignLibraryValueTypeadalah tipe di luar kendali Anda, yang didefinisikan dalam pustaka yang digunakan. -
MyForeignLibraryValueTypeSurrogateadalah pemetaan jenis pengganti keMyForeignLibraryValueType. -
RegisterConverterAttribute menentukan bahwa
MyForeignLibraryValueTypeSurrogateConverterbertindak sebagai pengonversi untuk memetakan antara dua jenis. Kelas mengimplementasikan IConverter<TValue,TSurrogate> antarmuka.
Orleans mendukung serialisasi jenis dalam hierarki jenis (jenis yang berasal dari jenis lain). Jika tipe asing mungkin muncul dalam hierarki tipe (misalnya, sebagai kelas dasar untuk salah satu tipe Anda sendiri), Anda juga harus mengimplementasikan antarmuka Orleans.IPopulator<TValue,TSurrogate>. Pertimbangkan contoh berikut:
// The foreign type is not sealed, allowing other types to inherit from it.
public class MyForeignLibraryType
{
public MyForeignLibraryType() { }
public MyForeignLibraryType(int num, string str, DateTimeOffset dto)
{
Num = num;
String = str;
DateTimeOffset = dto;
}
public int Num { get; set; }
public string String { get; set; }
public DateTimeOffset DateTimeOffset { get; set; }
}
// The surrogate is defined as it was in the previous example.
[GenerateSerializer]
public struct MyForeignLibraryTypeSurrogate
{
[Id(0)]
public int Num;
[Id(1)]
public string String;
[Id(2)]
public DateTimeOffset DateTimeOffset;
}
// Implement the IConverter and IPopulator interfaces on the converter.
[RegisterConverter]
public sealed class MyForeignLibraryTypeSurrogateConverter :
IConverter<MyForeignLibraryType, MyForeignLibraryTypeSurrogate>,
IPopulator<MyForeignLibraryType, MyForeignLibraryTypeSurrogate>
{
public MyForeignLibraryType ConvertFromSurrogate(
in MyForeignLibraryTypeSurrogate surrogate) =>
new(surrogate.Num, surrogate.String, surrogate.DateTimeOffset);
public MyForeignLibraryTypeSurrogate ConvertToSurrogate(
in MyForeignLibraryType value) =>
new()
{
Num = value.Num,
String = value.String,
DateTimeOffset = value.DateTimeOffset
};
public void Populate(
in MyForeignLibraryTypeSurrogate surrogate, MyForeignLibraryType value)
{
value.Num = surrogate.Num;
value.String = surrogate.String;
value.DateTimeOffset = surrogate.DateTimeOffset;
}
}
// Application types can inherit from the foreign type, assuming they're not sealed
// since Orleans knows how to serialize it.
[GenerateSerializer]
public sealed class DerivedFromMyForeignLibraryType : MyForeignLibraryType
{
public DerivedFromMyForeignLibraryType() { }
public DerivedFromMyForeignLibraryType(
int intValue, int num, string str, DateTimeOffset dto) : base(num, str, dto)
{
IntValue = intValue;
}
[Id(0)]
public int IntValue { get; set; }
}
Aturan pengelolaan versi
Toleransi versi didukung asalkan Anda mengikuti sekumpulan aturan saat memodifikasi jenis. Jika Anda terbiasa dengan sistem seperti Google Protocol Buffers (Protobuf), aturan ini akan akrab.
Jenis campuran (class & struct)
- Pewarisan didukung, tetapi memodifikasi hierarki warisan objek tidak didukung. Anda tidak dapat menambahkan, mengubah, atau menghapus kelas dasar kelas.
- Dengan pengecualian beberapa jenis numerik yang dijelaskan di bagian Numerik di bawah ini, Anda tidak dapat mengubah jenis bidang.
- Anda dapat menambahkan atau menghapus bidang kapan saja dalam hierarki pewarisan.
- Anda tidak dapat mengubah ID bidang.
- ID bidang harus unik untuk setiap tingkat dalam hierarki jenis tetapi dapat digunakan kembali antara kelas dasar dan subkelas. Misalnya,
Basekelas dapat mendeklarasikan bidang dengan ID0, danSub : Basekelas dapat mendeklarasikan bidang yang berbeda dengan ID yang sama,0.
Numerik
- Anda tidak dapat mengubah tanda tangan bidang numerik.
- Konversi antara
int&uinttidak valid.
- Konversi antara
- Anda dapat mengubah lebar bidang numerik.
- Misalnya, konversi dari
intkelongatauulongkeushortdidukung. - Konversi yang mempersempit rentang melemparkan pengecualian jika nilai waktu proses bidang menyebabkan overflow.
- Konversi dari
ulongkeushorthanya didukung jika nilai runtime kurang dariushort.MaxValue. - Konversi dari
doublekefloathanya didukung jika nilai runtime antarafloat.MinValuedanfloat.MaxValue. - Demikian pula untuk
decimal, yang memiliki rentang yang lebih sempit daripada dandoublefloat.
- Misalnya, konversi dari
Mesin Fotokopi
Orleans mempromosikan keamanan secara bawaan, termasuk keamanan dari beberapa jenis bug konkurensi. Secara khusus, Orleans segera menyalin objek yang diteruskan dalam panggilan grain secara bawaan. Orleans. Serialisasi memfasilitasi penyalinan ini. Saat Anda menerapkan Orleans.CodeGeneration.GenerateSerializerAttribute ke jenis, Orleans juga menghasilkan salinan untuk jenis tersebut. Orleans menghindari penyalinan jenis atau anggota individu yang ditandai dengan ImmutableAttribute. Untuk detail selengkapnya, lihat Serialisasi jenis yang tidak dapat diubah di Orleans.
Praktik terbaik serialisasi
✅ Berikan alias untuk jenis Anda dengan menggunakan atribut
[Alias("my-type")]. Jenis dengan alias dapat diganti namanya tanpa melanggar kompatibilitas.❌ Jangan mengubah
recordmenjadiclassyang biasa atau sebaliknya. Rekaman dan kelas tidak diwakili secara identik karena rekaman memiliki anggota konstruktor utama selain anggota reguler; oleh karena itu, keduanya tidak dapat dipertukarkan.❌ Jangan tambahkan tipe baru ke hierarki tipe yang sudah ada untuk jenis yang dapat diserialisasikan. Anda tidak boleh menambahkan kelas dasar baru ke tipe yang sudah ada. Anda dapat menambahkan subkelas baru dengan aman ke jenis yang sudah ada.
✅ Ganti penggunaan SerializableAttribute dengan GenerateSerializerAttribute dan deklarasi IdAttribute yang sesuai.
✅ Mulailah semua ID anggota dari nol untuk setiap jenis. ID dalam subkelas dan kelas dasarnya dapat tumpang tindih dengan aman. Kedua properti dalam contoh berikut memiliki ID yang sama dengan
0.[GenerateSerializer] public sealed class MyBaseClass { [Id(0)] public int MyBaseInt { get; set; } } [GenerateSerializer] public sealed class MySubClass : MyBaseClass { [Id(0)] public int MyBaseInt { get; set; } }✅ Lakukan perlebar jenis anggota numerik sesuai kebutuhan. Anda dapat memperlebar
sbytekeshortkeintkelong.- Anda dapat mempersempit jenis anggota numerik, tetapi menghasilkan pengecualian runtime jika nilai yang diamati tidak dapat diwakili dengan benar oleh jenis yang dipersempit. Misalnya,
int.MaxValuetidak dapat diwakili olehshortbidang, sehingga mempersempitintbidang untukshortdapat menghasilkan pengecualian runtime jika nilai seperti itu ditemui.
- Anda dapat mempersempit jenis anggota numerik, tetapi menghasilkan pengecualian runtime jika nilai yang diamati tidak dapat diwakili dengan benar oleh jenis yang dipersempit. Misalnya,
❌ Jangan mengubah sifat bertanda dari elemen tipe numerik. Anda tidak boleh mengubah jenis anggota dari
uintkeintatauintkeuint, misalnya.
Pengatur urutan penyimpanan biji-bijian
Orleans termasuk model persistensi yang didukung penyedia untuk grain, dapat diakses melalui properti State atau dengan menyuntikkan satu atau beberapa nilai IPersistentState<TState> ke dalam grain Anda. Sebelum Orleans 7.0, setiap penyedia memiliki mekanisme yang berbeda untuk mengonfigurasi serialisasi. Dalam Orleans 7.0, sekarang ada antarmuka serializer status butir tujuan umum, IGrainStorageSerializer, menawarkan cara yang konsisten untuk menyesuaikan serialisasi status untuk setiap penyedia. Penyedia penyimpanan yang didukung menerapkan pola yang melibatkan pengaturan IStorageProviderSerializerOptions.GrainStorageSerializer properti pada kelas opsi penyedia, misalnya:
- DynamoDBStorageOptions.GrainStorageSerializer
- AzureBlobStorageOptions.GrainStorageSerializer
- AzureTableStorageOptions.GrainStorageSerializer
- GrainStorageSerializer
Serialisasi penyimpanan grain saat ini default untuk Newtonsoft.Json membuat serialisasi status. Anda dapat mengganti ini dengan memodifikasi properti tersebut pada waktu konfigurasi. Contoh berikut menunjukkan ini menggunakan OptionsBuilder<TOptions>:
siloBuilder.AddAzureBlobGrainStorage(
"MyGrainStorage",
(OptionsBuilder<AzureBlobStorageOptions> optionsBuilder) =>
{
optionsBuilder.Configure<IMySerializer>(
(options, serializer) => options.GrainStorageSerializer = serializer);
});
Untuk informasi selengkapnya, lihat OptionsBuilder API.
Orleans memiliki kerangka kerja serialisasi yang canggih dan dapat diperluas. Orleans menserialisasikan jenis data yang diteruskan dalam pesan permintaan dan respons grain, serta objek status persisten biji-bijian. Sebagai bagian dari kerangka kerja ini, Orleans secara otomatis menghasilkan kode serialisasi untuk jenis data ini. Selain menghasilkan serialisasi/deserialisasi yang lebih efisien untuk jenis yang sudah .NET-serializable, Orleans juga mencoba menghasilkan serializer untuk jenis yang digunakan dalam antarmuka grain yang bukan .NET-serializable. Kerangka kerja ini juga mencakup serangkaian serializer bawaan yang efisien untuk jenis yang sering digunakan: daftar, kamus, string, primitif, array, dll.
Dua fitur penting dari Orleans'serializer membedakannya dari banyak kerangka kerja serialisasi pihak ketiga lainnya: jenis dinamis/polimorfisme arbitrer dan identitas objek.
Jenis dinamis dan polimorfisme arbitrer: Orleans tidak memberlakukan pembatasan pada jenis yang diteruskan dalam panggilan biji-bijian dan mempertahankan sifat dinamis dari jenis data aktual. Ini berarti, misalnya, jika suatu metode dalam antarmuka grain dinyatakan menerima IDictionary, tetapi pada runtime pengirim mengirimkan SortedDictionary<TKey,TValue>, penerima mendapatkan
SortedDictionary(meskipun antarmuka "kontrak statis"/grain tidak menentukan perilaku ini).Mempertahankan identitas objek: Jika objek yang sama diteruskan beberapa kali dalam argumen panggilan grain atau ditunjuk lebih dari sekali secara tidak langsung dari argumen, Orleans mengserialisasikannya hanya satu kali. Di sisi penerima, Orleans memulihkan semua referensi dengan benar sehingga dua pointer ke objek yang sama masih menunjuk ke objek yang sama setelah deserialisasi. Mempertahankan identitas objek penting dalam skenario seperti berikut: Bayangkan grain A mengirim kamus dengan 100 entri ke grain B, dan 10 kunci di kamus menunjuk ke objek yang sama,
obj, di sisi A. Tanpa mempertahankan identitas objek, B akan menerima kamus 100 entri dengan 10 kunci yang menunjuk ke 10 klon yang berbeda dariobj. Dengan identitas objek yang dipertahankan, kamus di sisi B terlihat persis seperti di sisi A, dengan 10 kunci tersebut menunjuk ke satu objekobj.
Serializer biner .NET standar menyediakan dua perilaku di atas, jadi penting bagi kami untuk mendukung perilaku Orleans standar dan akrab ini juga.
Serializer yang dihasilkan
Orleans menggunakan aturan berikut untuk memutuskan serializer mana yang akan dihasilkan:
- Pindai semua jenis dalam semua perakitan yang mereferensikan pustaka inti Orleans.
- Dari rakitan tersebut, hasilkan serializer untuk jenis yang langsung direferensikan dalam tanda tangan metode antarmuka grain atau tanda tangan kelas status, atau untuk jenis apa pun yang ditandai dengan SerializableAttribute.
- Selain itu, antarmuka grain atau proyek implementasi dapat mengarah kepada tipe acak dalam proses pembuatan serialisasi dengan menambahkan KnownTypeAttribute atau KnownAssemblyAttribute atribut pada tingkat perakitan. Ini memberi tahu generator kode untuk menghasilkan serializer untuk tipe tertentu atau semua tipe yang memenuhi syarat dalam assembly. Untuk informasi selengkapnya tentang atribut tingkat rakitan, lihat Menerapkan atribut di tingkat perakitan.
Serialisasi fallback
Orleans mendukung transmisi jenis arbitrer saat runtime. Oleh karena itu, generator kode bawaan tidak dapat menentukan seluruh set jenis yang akan dikirimkan sebelumnya. Selain itu, jenis tertentu tidak dapat memiliki serializer yang dihasilkan untuk mereka karena tidak dapat diakses (misalnya, private) atau memiliki bidang yang tidak dapat diakses (misalnya, readonly). Dengan demikian, ada kebutuhan untuk serialisasi jenis just-in-time yang tidak terduga atau tidak dapat memiliki serializer yang dihasilkan sebelumnya. Serializer yang bertanggung jawab atas jenis-jenis ini disebut fallback serializer.
Orleans menyertakan dua serializer cadangan:
- Orleans.Serialization.BinaryFormatterSerializer, yang menggunakan . NET ; BinaryFormatterdan
-
Orleans.Serialization.ILBasedSerializer, yang memancarkan instruksi CIL pada runtime untuk membuat serializer yang memanfaatkan kerangka kerja serialisasi Orleans untuk melakukan serialisasi pada setiap bidang. Ini berarti bahwa jika jenis
MyPrivateTypeyang tidak dapat diakses berisi bidangMyTypeyang memiliki serializer kustom, serializer kustom tersebut akan digunakan untuk menserialisasikannya.
Konfigurasikan serializer fallback dengan menggunakan properti FallbackSerializationProvider pada ClientConfiguration (klien) dan GlobalConfiguration (silo).
// Client configuration
var clientConfiguration = new ClientConfiguration();
clientConfiguration.FallbackSerializationProvider =
typeof(FantasticSerializer).GetTypeInfo();
// Global configuration
var globalConfiguration = new GlobalConfiguration();
globalConfiguration.FallbackSerializationProvider =
typeof(FantasticSerializer).GetTypeInfo();
Atau, tentukan penyedia serialisasi fallback dalam konfigurasi XML:
<Messaging>
<FallbackSerializationProvider
Type="GreatCompany.FantasticFallbackSerializer, GreatCompany.SerializerAssembly"/>
</Messaging>
BinaryFormatterSerializer ini adalah serializer fallback default.
Peringatan
Serialisasi biner dengan BinaryFormatter bisa berbahaya. Untuk informasi selengkapnya, lihat panduan keamanan BinaryFormatter dan panduan migrasi BinaryFormatter.
Serialisasi pengecualian
Pengecualian diserialisasikan menggunakan serializer cadangan. Dengan konfigurasi default, BinaryFormatter adalah serializer fallback. Oleh karena itu, Anda harus mengikuti pola ISerializable untuk memastikan serialisasi yang benar atas semua properti dalam jenis pengecualian.
Berikut adalah contoh jenis pengecualian dengan serialisasi yang diimplementasikan dengan benar:
[Serializable]
public class MyCustomException : Exception
{
public string MyProperty { get; }
public MyCustomException(string myProperty, string message)
: base(message)
{
MyProperty = myProperty;
}
public MyCustomException(string transactionId, string message, Exception innerException)
: base(message, innerException)
{
MyProperty = transactionId;
}
// Note: This is the constructor called by BinaryFormatter during deserialization
public MyCustomException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
MyProperty = info.GetString(nameof(MyProperty));
}
// Note: This method is called by BinaryFormatter during serialization
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue(nameof(MyProperty), MyProperty);
}
}
Praktik terbaik serialisasi
Serialisasi melayani dua tujuan utama dalam Orleans:
- Sebagai format kawat untuk mengirimkan data antara biji-bijian dan klien saat runtime.
- Sebagai format penyimpanan untuk mempertahankan data berumur panjang yang dapat diambil di kemudian hari.
Serializer yang dihasilkan oleh Orleans cocok untuk tujuan pertama karena fleksibilitas, performa, dan versatilitasnya. Mereka tidak cocok untuk tujuan kedua karena mereka tidak toleran versi secara eksplisit. Sebaiknya konfigurasikan serializer yang toleran terhadap versi, seperti Protocol Buffers, untuk data yang tetap. Protokol Buffer didukung melalui Orleans.Serialization.ProtobufSerializer dari paket NuGet Microsoft.Orleans.OrleansGoogleUtils. Ikuti praktik terbaik untuk serializer yang Anda pilih untuk memastikan toleransi versi. Konfigurasikan serializer pihak ketiga menggunakan SerializationProviders properti konfigurasi seperti yang dijelaskan di atas.