Udostępnij za pośrednictwem


Serializacja w programie Orleans

Istnieją zasadniczo dwa rodzaje serializacji używane w programie Orleans:

  • Serializacja wywołań ziarna — używana do serializacji obiektów przekazywanych do i z ziarna.
  • Serializacja magazynu ziarna — służy do serializacji obiektów do i z systemów magazynowania.

Większość tego artykułu jest przeznaczona do serializacji wywołań ziarna za pośrednictwem struktury serializacji zawartej w programie Orleans. W sekcji Serializatory magazynowania ziarna omówiono serializacji magazynowania ziarna.

Użyj Orleans serializacji

Orleans zawiera zaawansowaną i rozszerzalną strukturę serializacji, która może być nazywana . Orleans Serializacja. Struktura serializacji zawarta w programie Orleans została zaprojektowana w celu spełnienia następujących celów:

  • Wysoka wydajność — serializator został zaprojektowany i zoptymalizowany pod kątem wydajności. Więcej szczegółów można znaleźć w tej prezentacji.
  • Wysoka wierność — serializator wiernie reprezentuje większość elementów . System typów platformy NET, w tym obsługa typów ogólnych, polimorfizm, hierarchie dziedziczenia, tożsamość obiektu i cykliczne grafy. Wskaźniki nie są obsługiwane, ponieważ nie są przenośne między procesami.
  • Elastyczność — serializator można dostosować do obsługi bibliotek innych firm przez utworzenie zastępców lub delegowanie do zewnętrznych bibliotek serializacji, takich jak System.Text.Json, Newtonsoft.Json i Google.Protobuf.
  • Tolerancja wersji — serializator umożliwia rozwijanie typów aplikacji wraz z upływem czasu i obsługę:
    • Dodawanie i usuwanie członków
    • Podklasowanie
    • Rozszerzanie i zawężanie liczbowe (np int . do/z long, float do/z double)
    • Zmienianie nazw typów

Reprezentacja typów o wysokiej wierności jest dość rzadka dla serializatorów, więc niektóre punkty uzasadniają dalsze opracowanie:

  1. Typy dynamiczne i dowolny polimorfizm: Orleans nie wymusza ograniczeń typów, które mogą być przekazywane w wywołaniach ziarna i utrzymują dynamiczny charakter rzeczywistego typu danych. Oznacza to na przykład, że jeśli metoda w interfejsach ziarna jest zadeklarowana do akceptowania IDictionary , ale w czasie wykonywania, nadawca SortedDictionary<TKey,TValue>przekazuje , odbiorca rzeczywiście otrzyma SortedDictionary (chociaż interfejs "kontrakt statyczny"/ziarno nie określił tego zachowania).

  2. Utrzymywanie tożsamości obiektu: jeśli ten sam obiekt jest przekazywany wiele typów w argumentach wywołania ziarna lub jest pośrednio wskazywany więcej niż raz z argumentów, Orleans serializuje go tylko raz. Po stronie odbiorcy wszystkie odwołania zostaną poprawnie przywrócone, Orleans tak aby dwa wskaźniki do tego samego obiektu nadal wskazywały ten sam obiekt po deserializacji. Tożsamość obiektu jest ważna do zachowania w scenariuszach, takich jak poniżej. Imagine grain A wysyła słownik z 100 wpisów do ziarna B, a 10 kluczy w słowniku wskazuje ten sam obiekt, obj, po stronie A. Bez zachowania tożsamości obiektu B otrzyma słownik zawierający 100 wpisów z tymi 10 kluczami wskazującymi na 10 różnych klonów objobiektu . W przypadku zachowania tożsamości obiektu słownik po stronie B wygląda dokładnie tak samo jak po stronie A z tymi 10 kluczami wskazującymi pojedynczy obiekt obj. Należy pamiętać, że ponieważ domyślne implementacje kodu skrótu ciągu na platformie .NET są losowe dla poszczególnych procesów, kolejność wartości w słownikach i zestawach skrótów (na przykład) może nie być zachowywana.

Aby zapewnić tolerancję wersji, serializator wymaga, aby deweloperzy mogli jawnie określać, które typy i składowe są serializowane. Staraliśmy się to jak najbardziej bezboleśnie. Aby poinstruować Orleans generowanie kodu serializatora dla danego typu, należy oznaczyć wszystkie typy Orleans.GenerateSerializerAttribute możliwe do serializacji. Po wykonaniu tej czynności możesz użyć dołączonej poprawki kodu, aby dodać wymagane Orleans.IdAttribute do serializacji elementów członkowskich w typach, jak pokazano tutaj:

Animowany obraz sugerowanej poprawki kodu dostępnej i zastosowany do atrybutu GenerateSerializerAttribute, gdy typ zawierający nie zawiera elementów członkowskich identyfikatora IdAttribute.

Oto przykład typu z możliwością serializacji w Orleanspliku , pokazujący sposób stosowania atrybutów.

[GenerateSerializer]
public class Employee
{
    [Id(0)]
    public string Name { get; set; }
}

Orleans obsługuje dziedziczenie i serializuje poszczególne warstwy w hierarchii oddzielnie, co umożliwia im posiadanie unikatowych identyfikatorów składowych.

[GenerateSerializer]
public class Publication
{
    [Id(0)]
    public string Title { get; set; }
}

[GenerateSerializer]
public class Book : Publication
{
    [Id(0)]
    public string ISBN { get; set; }
}

W poprzednim kodzie należy pamiętać, że zarówno Publication elementy członkowskie, jak i Book z elementami [Id(0)] , mimo że Book pochodzą z Publicationelementu . Jest to zalecana praktyka, Orleans ponieważ identyfikatory elementów członkowskich są ograniczone do poziomu dziedziczenia, a nie całego typu. Składowe można dodawać i usuwać z Publication i Book niezależnie, ale nie można wstawić nowej klasy bazowej do hierarchii po wdrożeniu aplikacji bez specjalnej uwagi.

Orleans Obsługuje również serializacji typów z elementami internal, privatei readonly , takimi jak w tym przykładzie:

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

Domyślnie serializuje typ, Orleans kodując jego pełną nazwę. Możesz to zastąpić, dodając element Orleans.AliasAttribute. Spowoduje to serializacji typu przy użyciu nazwy odpornej na zmianę nazwy bazowej klasy lub przeniesienie jej między zestawami. Aliasy typów są globalnie ograniczone i nie można mieć dwóch aliasów o tej samej wartości w aplikacji. W przypadku typów ogólnych wartość aliasu musi zawierać liczbę parametrów ogólnych poprzedzonych na przykład backtick, MyGenericType<T, U> może mieć alias [Alias("mytype`2")].

Serializacji record typów

Składowe zdefiniowane w konstruktorze podstawowym rekordu mają domyślnie niejawne identyfikatory. Innymi słowy, Orleans obsługuje serializacji record typów. Oznacza to, że nie można zmienić kolejności parametrów dla już wdrożonego typu, ponieważ przerywa to zgodność z poprzednimi wersjami aplikacji (w przypadku uaktualnienia stopniowego) i z serializowanymi wystąpieniami tego typu w magazynie i strumieniach. Elementy członkowskie zdefiniowane w treści typu rekordu nie współużytkują tożsamości z podstawowymi parametrami konstruktora.

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

Jeśli nie chcesz, aby podstawowe parametry konstruktora były automatycznie dołączane jako pola możliwe do serializacji, możesz użyć polecenia [GenerateSerializer(IncludePrimaryConstructorParameters = false)].

Zastępcze do serializacji typów obcych

Czasami może być konieczne przekazanie typów między ziarnami, nad którymi nie masz pełnej kontroli. W takich przypadkach konwersja na i z określonego typu niestandardowego w kodzie aplikacji może być niepraktyczna. Orleans oferuje rozwiązanie tych sytuacji w postaci typów zastępczych. Zastępcze są serializowane zamiast ich typu docelowego i mają funkcje konwersji na i z typu docelowego. Rozważmy następujący przykład typu obcego i odpowiadającego mu zastępczego i konwertera:

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

Powyższy kod:

  • Jest MyForeignLibraryValueType to typ spoza kontrolki zdefiniowany w bibliotece zużywanej.
  • Jest MyForeignLibraryValueTypeSurrogate typem zastępczym, który mapuje na MyForeignLibraryValueType.
  • Określa RegisterConverterAttribute , że MyForeignLibraryValueTypeSurrogateConverter działa jako konwerter do mapowania na i z dwóch typów. Klasa jest implementacją interfejsu IConverter<TValue,TSurrogate> .

Orleans obsługuje serializacji typów w hierarchiach typów (typy pochodzące z innych typów). W przypadku, gdy typ obcy może pojawić się w hierarchii typów (na przykład jako klasa bazowa dla jednego z własnych typów), należy dodatkowo zaimplementować Orleans.IPopulator<TValue,TSurrogate> interfejs. Rozważmy następujący przykład:

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

Reguły przechowywania wersji

Tolerancja wersji jest obsługiwana, jeśli deweloper stosuje zestaw reguł podczas modyfikowania typów. Jeśli deweloper zna systemy, takie jak protokołu Google (Protobuf), te reguły będą znane.

Typy złożone (class & struct)

  • Dziedziczenie jest obsługiwane, ale modyfikowanie hierarchii dziedziczenia obiektu nie jest obsługiwane. Nie można dodać klasy bazowej klasy, zmienić jej na inną lub usunąć.
  • Z wyjątkiem niektórych typów liczbowych, opisanych w poniższej sekcji Numeryczne , nie można zmienić typów pól.
  • Pola można dodawać lub usuwać w dowolnym momencie w hierarchii dziedziczenia.
  • Nie można zmienić identyfikatorów pól.
  • Identyfikatory pól muszą być unikatowe dla każdego poziomu w hierarchii typów, ale mogą być używane ponownie między klasami bazowymi i podkatałami. Na przykład Base klasa może zadeklarować pole o identyfikatorze0, a inne pole może być zadeklarowane za pomocą Sub : Base tego samego identyfikatora. 0

Wartości numeryczne

  • Nie można zmienić podpisu pola liczbowego.
    • Konwersje między elementami int i uint są nieprawidłowe.
  • Szerokość pola liczbowego można zmienić.
    • Na przykład: obsługiwane są konwersje z int do long lub ulong do ushort .
    • Konwersje, które zawężają szerokość, będą zgłaszane, jeśli wartość środowiska uruchomieniowego pola spowoduje przepełnienie.
      • Konwersja z ulong na ushort jest obsługiwana tylko wtedy, gdy wartość w czasie wykonywania jest mniejsza niż ushort.MaxValue.
      • Konwersje z double do float są obsługiwane tylko wtedy, gdy wartość środowiska uruchomieniowego mieści się między float.MinValue i float.MaxValue.
      • Podobnie w przypadku elementu decimal, który ma węższy zakres niż i double float.

Kopiarki

Orleans domyślnie promuje bezpieczeństwo. Obejmuje to bezpieczeństwo niektórych klas błędów współbieżności. W szczególności Orleans natychmiast skopiuje obiekty przekazywane w wywołaniach ziarna domyślnie. To kopiowanie jest obsługiwane przez program Orleans. Serializacja i zastosowanie Orleans.CodeGeneration.GenerateSerializerAttribute do typu Orleans spowoduje również wygenerowanie kopii dla tego typu. Orleans Pozwoli uniknąć kopiowania typów lub pojedynczych elementów członkowskich oznaczonych przy użyciu elementu ImmutableAttribute. Aby uzyskać więcej informacji, zobacz Serializacja niezmiennych typów w programie Orleans.

Najlepsze rozwiązania dotyczące serializacji

  • Podaj aliasy typów przy użyciu atrybutu [Alias("my-type")] . Nazwy typów z aliasami można zmienić bez niezgodności.

  • Nie zmieniaj record obiektu na zwykły class lub odwrotnie. Rekordy i klasy nie są reprezentowane w identyczny sposób, ponieważ rekordy mają podstawowe elementy członkowskie konstruktora oprócz zwykłych składowych i dlatego te dwa nie są wymienne.

  • Nie dodawaj nowych typów do istniejącej hierarchii typów dla typu możliwego do serializacji. Nie można dodać nowej klasy bazowej do istniejącego typu. Możesz bezpiecznie dodać nową podklasę do istniejącego typu.

  • Zastąp wartości użycia SerializableAttribute elementem i GenerateSerializerAttribute odpowiednimi IdAttribute deklaracjami.

  • Uruchom wszystkie identyfikatory składowych o wartości zero dla każdego typu. Identyfikatory w podklasie i jej klasie bazowej mogą bezpiecznie nakładać się na siebie. Obie właściwości w poniższym przykładzie mają identyfikatory równe 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; }
    }
    
  • W razie potrzeby należy poszerzyć typy składowych liczbowych. Możesz poszerz sbyte do short .longint

    • Można zawęzić typy składowych liczbowych, ale spowoduje wyjątek środowiska uruchomieniowego, jeśli obserwowane wartości nie mogą być poprawnie reprezentowane przez zawężonego typu. Na przykład int.MaxValue nie może być reprezentowana przez short pole, więc zawężenie int pola w celu short może spowodować wyjątek środowiska uruchomieniowego, jeśli taka wartość została napotkana.
  • Nie zmieniaj podpisu elementu członkowskiego typu liczbowego. Na przykład nie można zmienić typu elementu członkowskiego z uint na int lub na int uintwartość , na przykład.

Serializatory magazynu ziarna

Orleans Zawiera model trwałości opartej na dostawcy dla ziarna, do którego uzyskuje dostęp za pośrednictwem State właściwości lub przez wstrzyknięcie jednej lub większej liczby IPersistentState<TState> wartości do ziarna. Przed Orleans 7.0 każdy dostawca miał inny mechanizm konfigurowania serializacji. W Orleans wersji 7.0 istnieje teraz interfejs serializatora stanu ziarna ogólnego przeznaczenia, IGrainStorageSerializerktóry oferuje spójny sposób dostosowywania serializacji stanu dla każdego dostawcy. Obsługiwani dostawcy magazynu implementują wzorzec, który obejmuje ustawienie IStorageProviderSerializerOptions.GrainStorageSerializer właściwości w klasie opcji dostawcy, na przykład:

Serializacja magazynu ziarna jest obecnie domyślnie ustawiona na Newtonsoft.Json serializacji stanu. Tę właściwość można zastąpić, modyfikując tę właściwość w czasie konfiguracji. W poniższym przykładzie pokazano to przy użyciu polecenia OptionsBuilder<TOptions>:

siloBuilder.AddAzureBlobGrainStorage(
    "MyGrainStorage",
    (OptionsBuilder<AzureBlobStorageOptions> optionsBuilder) =>
    {
        optionsBuilder.Configure<IMySerializer>(
            (options, serializer) => options.GrainStorageSerializer = serializer);
    });

Aby uzyskać więcej informacji, zobacz OptionsBuilder API (Interfejs API programu OptionsBuilder).

Orleans ma zaawansowaną i rozszerzalną strukturę serializacji. Orleans serializuje typy danych przekazywane w komunikatach żądania ziarna i odpowiedzi, a także obiekty stanu trwałego ziarna. W ramach tej struktury Orleans automatycznie generuje kod serializacji dla tych typów danych. Oprócz generowania bardziej wydajnej serializacji/deserializacji typów, które są już . Z możliwością Orleans serializacji net próbuje również wygenerować serializatory dla typów używanych w interfejsach ziarna, które nie są . Z możliwością serializacji net. Struktura zawiera również zestaw wydajnych wbudowanych serializatorów dla często używanych typów: list, słowników, ciągów, elementów pierwotnych, tablic itp.

Dwie ważne cechy Orleansserializatora odróżniają ją od wielu innych struktur serializacji innych firm: dynamiczne typy/dowolną polimorfizm i tożsamość obiektów.

  1. Typy dynamiczne i dowolny polimorfizm: Orleans nie wymusza ograniczeń typów, które mogą być przekazywane w wywołaniach ziarna i utrzymują dynamiczny charakter rzeczywistego typu danych. Oznacza to na przykład, że jeśli metoda w interfejsach ziarna jest zadeklarowana do akceptowania IDictionary , ale w czasie wykonywania, nadawca SortedDictionary<TKey,TValue>przekazuje , odbiorca rzeczywiście otrzyma SortedDictionary (chociaż interfejs "kontrakt statyczny"/ziarno nie określił tego zachowania).

  2. Utrzymywanie tożsamości obiektu: jeśli ten sam obiekt jest przekazywany wiele typów w argumentach wywołania ziarna lub jest pośrednio wskazywany więcej niż raz z argumentów, Orleans serializuje go tylko raz. Po stronie odbiorcy wszystkie odwołania zostaną poprawnie przywrócone, Orleans tak aby dwa wskaźniki do tego samego obiektu nadal wskazywały ten sam obiekt po deserializacji. Tożsamość obiektu jest ważna do zachowania w scenariuszach, takich jak poniżej. Imagine grain A wysyła słownik z 100 wpisów do ziarna B, a 10 kluczy w słowniku wskazuje ten sam obiekt, obj, po stronie A. Bez zachowania tożsamości obiektu B otrzyma słownik zawierający 100 wpisów z tymi 10 kluczami wskazującymi 10 różnych klonów obj. W przypadku zachowania tożsamości obiektu słownik po stronie B wygląda dokładnie tak samo jak po stronie A z tymi 10 kluczami wskazującymi pojedynczy obiekt obj.

Powyższe dwa zachowania są udostępniane przez standardowy serializator binarny platformy .NET i dlatego ważne było, abyśmy również obsługiwali ten standard i znane zachowanie Orleans .

Wygenerowane serializatory

Orleans Używa następujących reguł, aby zdecydować, które serializatory mają być generowane. Reguły to:

  1. Skanuj wszystkie typy we wszystkich zestawach, które odwołują się do biblioteki podstawowej Orleans .
  2. Z tych zestawów: generuj serializatory dla typów, do których bezpośrednio odwołuje się sygnatury metody ziarna lub sygnatury klasy stanu lub dla dowolnego typu oznaczonego znakiem SerializableAttribute.
  3. Ponadto interfejs ziarna lub projekt implementacji może wskazywać na dowolne typy generowania serializacji, dodając KnownTypeAttribute atrybuty lub na poziomie zestawu, aby poinformować generator kodu o generowaniu serializatorów dla określonych typów lub KnownAssemblyAttribute wszystkich kwalifikujących się typów w zestawie. Aby uzyskać więcej informacji na temat atrybutów na poziomie zestawu, zobacz Stosowanie atrybutów na poziomie zestawu.

Serializacja rezerwowa

Orleans obsługuje przesyłanie dowolnych typów w czasie wykonywania, dlatego wbudowany generator kodu nie może określić całego zestawu typów, które będą przesyłane z wyprzedzeniem. Ponadto niektóre typy nie mogą mieć wygenerowanych serializatorów, ponieważ są niedostępne (na przykład ) lub mają niedostępne pola (na przykład privatereadonly). W związku z tym istnieje potrzeba serializacji typu just in time, które były nieoczekiwane lub nie mogły być generowane serializatory z wyprzedzeniem. Serializator odpowiedzialny za te typy jest nazywany serializatorem rezerwowym. Orleans dostarczanych z dwoma serializatorami rezerwowymi:

Serializator rezerwowy można skonfigurować przy użyciu FallbackSerializationProvider właściwości zarówno ClientConfiguration na kliencie, jak i GlobalConfiguration na silosach.

// Client configuration
var clientConfiguration = new ClientConfiguration();
clientConfiguration.FallbackSerializationProvider =
    typeof(FantasticSerializer).GetTypeInfo();

// Global configuration
var globalConfiguration = new GlobalConfiguration();
globalConfiguration.FallbackSerializationProvider =
    typeof(FantasticSerializer).GetTypeInfo();

Alternatywnie można określić rezerwowego dostawcę serializacji w konfiguracji XML:

<Messaging>
    <FallbackSerializationProvider
        Type="GreatCompany.FantasticFallbackSerializer, GreatCompany.SerializerAssembly"/>
</Messaging>

Jest BinaryFormatterSerializer to domyślny serializator rezerwowy.

Ostrzeżenie

Serializacja binarna z BinaryFormatter programem może być niebezpieczna. Aby uzyskać więcej informacji, zobacz Przewodnik po zabezpieczeniach BinaryFormatter i Przewodnik migracji BinaryFormatter.

Serializacja wyjątków

Wyjątki są serializowane przy użyciu serializatora rezerwowego. Przy użyciu konfiguracji domyślnej jest serializator rezerwowy i BinaryFormatter dlatego należy przestrzegać wzorca ISerializable, aby zapewnić poprawną serializacji wszystkich właściwości w typie wyjątku.

Oto przykład typu wyjątku z poprawnie zaimplementowaną serializacji:

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

Najlepsze rozwiązania dotyczące serializacji

Serializacja służy dwóm podstawowym celom w programie Orleans:

  1. Jako format przewodu do przesyłania danych między ziarnami i klientami w czasie wykonywania.
  2. Jako format magazynu do utrwalania długotrwałych danych na potrzeby późniejszego pobierania.

Serializatory generowane przez Orleans program są odpowiednie do pierwszego celu ze względu na ich elastyczność, wydajność i wszechstronność. Nie są one tak odpowiednie do drugiego celu, ponieważ nie są jawnie odporne na wersje. Zaleca się, aby użytkownicy konfigurowali serializator odporny na wersje, taki jak protokołu dla danych trwałych. protokołu są obsługiwane za pośrednictwem Orleans.Serialization.ProtobufSerializer programu Microsoft.Orleans. OrleansGoogleUtils pakiet NuGet. Najlepsze rozwiązania dotyczące wybranego serializatora powinny być stosowane w celu zapewnienia tolerancji wersji. Serializatory innych firm można skonfigurować przy użyciu właściwości konfiguracji zgodnie z SerializationProviders powyższym opisem.