Delen via


Serialisatie in Orleans

Er zijn in grote lijnen twee soorten serialisatie gebruikt in Orleans:

  • Serialisatie van korrels- wordt gebruikt voor het serialiseren van objecten die worden doorgegeven aan en van korrels.
  • Korrelopslagserialisatie : wordt gebruikt voor het serialiseren van objecten naar en van opslagsystemen.

Het grootste deel van dit artikel is gewijd aan serialisatie van aanroepen via het serialisatieframework dat is opgenomen in Orleans. In de sectie Grain Storage Serializers wordt de serialisatie van graanopslag besproken.

Serialisatie gebruiken Orleans

Orleans bevat een geavanceerd en uitbreidbaar serialisatieframework dat kan worden aangeduid als Orleans. Serialisatie. Het serialisatieframework dat is opgenomen in Orleans , is ontworpen om te voldoen aan de volgende doelstellingen:

  • Hoge prestaties : de serializer is ontworpen en geoptimaliseerd voor prestaties. In deze presentatie vindt u meer informatie.
  • High-fidelity - De serializer vertegenwoordigt het merendeel van . Het typesysteem van NET, waaronder ondersteuning voor generics, polymorfisme, overnamehiërarchieën, objectidentiteit en cyclische grafieken. Aanwijzers worden niet ondersteund, omdat ze niet overdraagbaar zijn tussen processen.
  • Flexibiliteit: de serialisatiefunctie kan worden aangepast om bibliotheken van derden te ondersteunen door surrogaten te maken of delegeren aan externe serialisatiebibliotheken zoals System.Text.Json, Newtonsoft.Json en Google.Protobuf.
  • Versietolerantie : met de serializer kunnen toepassingstypen zich in de loop van de tijd ontwikkelen, waarbij ondersteuning wordt geboden voor:
    • Leden toevoegen en verwijderen
    • Subklassen
    • Numerieke widening en narrowing (bijvoorbeeld int naar/van long, float naar/van double)
    • Naamgevingstypen wijzigen

Hoogwaardige weergave van typen is vrij ongebruikelijk voor serialisatieprogramma's, dus sommige punten rechtvaardigen verdere uitwerking:

  1. Dynamische typen en willekeurige polymorfisme: Orleans dwingt geen beperkingen af voor de typen die kunnen worden doorgegeven in graanoproepen en houdt de dynamische aard van het werkelijke gegevenstype bij. Dat betekent bijvoorbeeld dat als de methode in de graaninterfaces wordt gedeclareerd om te accepteren IDictionary , maar tijdens runtime de afzender passeert SortedDictionary<TKey,TValue>, de ontvanger inderdaad krijgt SortedDictionary (hoewel de "statische contract"/grain-interface dit gedrag niet heeft opgegeven).

  2. Objectidentiteit behouden: als hetzelfde object meerdere typen in de argumenten van een aanroep van een graan wordt doorgegeven of indirect meer dan één keer wordt verwezen vanuit de argumenten, Orleans wordt het slechts één keer geserialiseerd. Aan de ontvangerzijde Orleans worden alle verwijzingen correct hersteld, zodat twee aanwijzers naar hetzelfde object nog steeds verwijzen na deserialisatie. Objectidentiteit is belangrijk om te behouden in scenario's zoals de volgende. Stel dat grain A een woordenlijst met 100 vermeldingen naar korrel B verzendt en 10 sleutels in de woordenlijst naar hetzelfde object wijst, objaan de zijde van A. Zonder de objectidentiteit te behouden, ontvangt B een woordenlijst van 100 vermeldingen met die 10 sleutels die verwijzen naar 10 verschillende klonen van obj. Met objectidentiteit behouden, ziet de woordenlijst aan de zijkant van B er precies zo uit als aan de zijde van A met die 10 sleutels die naar één object objwijzen. Houd er rekening mee dat de volgorde van waarden in woordenlijsten en hashsets (bijvoorbeeld) niet behouden blijft omdat de standaard-hashcode-implementaties in .NET willekeurig zijn per proces.

Voor de ondersteuning van versietolerantie moeten ontwikkelaars expliciet zijn over welke typen en leden worden geserialiseerd. We hebben geprobeerd dit zo pijnvrij mogelijk te maken. U moet alle serialiseerbare typen markeren om Orleans.GenerateSerializerAttribute de serialisatiecode Orleans voor uw type te genereren. Zodra u dit hebt gedaan, kunt u de meegeleverde code-fix gebruiken om de vereiste Orleans.IdAttribute toe te voegen aan de serialiseerbare leden van uw typen, zoals hier wordt weergegeven:

Een geanimeerde afbeelding van de beschikbare codefix die wordt voorgesteld en toegepast op generateSerializerAttribute wanneer het betreffende type geen IdAttribute's op de leden bevat.

Hier volgt een voorbeeld van een serialiseerbare type waarin Orleanswordt gedemonstreerd hoe u de kenmerken toepast.

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

Orleans ondersteunt overname en serialiseert de afzonderlijke lagen in de hiërarchie afzonderlijk, zodat ze afzonderlijke lid-id's kunnen hebben.

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

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

In de voorgaande code moet u er rekening mee houden dat beide Publication en leden hebben met[Id(0)], ook al Book zijn ze afgeleid van PublicationBook . Dit is de aanbevolen procedure omdat Orleans leden-id's zijn afgestemd op het overnameniveau, niet het type als geheel. Leden kunnen worden toegevoegd aan en onafhankelijk van Publication elkaar Book worden verwijderd, maar een nieuwe basisklasse kan niet in de hiërarchie worden ingevoegd zodra de toepassing is geïmplementeerd zonder speciale aandacht.

Orleans ondersteunt ook serialisatietypen met internal, privateen readonly leden, zoals in dit voorbeeldtype:

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

Orleans Standaard wordt uw type geserialiseerd door de volledige naam te coderen. U kunt dit overschrijven door een Orleans.AliasAttribute. Als u dit doet, wordt uw type geserialiseerd met behulp van een naam die bestand is tegen het wijzigen van de naam van de onderliggende klasse of het verplaatsen ervan tussen assembly's. Typealiassen zijn globaal gericht en u kunt geen twee aliassen met dezelfde waarde in een toepassing hebben. Voor algemene typen moet de aliaswaarde het aantal algemene parameters bevatten dat voorafgaat door een backtick, MyGenericType<T, U> bijvoorbeeld de alias [Alias("mytype`2")].

Typen serialiseren record

Leden die zijn gedefinieerd in de primaire constructor van een record, hebben standaard impliciete id's. Met andere woorden, Orleans ondersteunt serialisatietypen record . Dit betekent dat u de parametervolgorde voor een al geïmplementeerd type niet kunt wijzigen, omdat dat de compatibiliteit met eerdere versies van uw toepassing (in het geval van een rolling upgrade) en met geserialiseerde exemplaren van dat type in opslag en streams wordt verbroken. Leden die zijn gedefinieerd in de hoofdtekst van een recordtype, delen geen identiteiten met de primaire constructorparameters.

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

Als u niet wilt dat de primaire constructorparameters automatisch worden opgenomen als serialiseerbare velden, kunt u dit gebruiken [GenerateSerializer(IncludePrimaryConstructorParameters = false)].

Surrogaten voor het serialiseren van vreemde typen

Soms moet u typen doorgeven tussen korrels waarvoor u geen volledige controle hebt. In dergelijke gevallen kan het niet praktisch zijn om handmatig te converteren naar en van een aangepast type in uw toepassingscode. Orleans biedt een oplossing voor deze situaties in de vorm van surrogaattypen. Surrogaten worden geserialiseerd in plaats van hun doeltype en hebben functionaliteit om van en naar het doeltype te converteren. Bekijk het volgende voorbeeld van een vreemd type en een bijbehorende surrogaat en conversieprogramma:

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

In de voorgaande code:

  • Het MyForeignLibraryValueType is een type buiten uw besturingselement, gedefinieerd in een verbruikende bibliotheek.
  • Het MyForeignLibraryValueTypeSurrogate is een surrogaattype dat wordt toegewezen aan MyForeignLibraryValueType.
  • De RegisterConverterAttribute geeft aan dat het MyForeignLibraryValueTypeSurrogateConverter fungeert als een conversieprogramma dat moet worden toegewezen aan en van de twee typen. De klasse is een implementatie van de IConverter<TValue,TSurrogate> interface.

Orleans ondersteunt serialisatie van typen in typehiërarchieën (typen die zijn afgeleid van andere typen). In het geval dat een refererend type kan worden weergegeven in een typehiërarchie (bijvoorbeeld als de basisklasse voor een van uw eigen typen), moet u de Orleans.IPopulator<TValue,TSurrogate> interface ook implementeren. Kijk een naar het volgende voorbeeld:

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

Versiebeheerregels

Versietolerantie wordt ondersteund op voorwaarde dat de ontwikkelaar een set regels volgt bij het wijzigen van typen. Als de ontwikkelaar bekend is met systemen zoals Google Protocol Buffers (Protobuf), zijn deze regels bekend.

Samengestelde typen (class & struct)

  • Overname wordt ondersteund, maar het wijzigen van de overnamehiërarchie van een object wordt niet ondersteund. De basisklasse van een klasse kan niet worden toegevoegd, gewijzigd in een andere klasse of worden verwijderd.
  • Met uitzondering van sommige numerieke typen, zoals beschreven in de sectie Numeriek hieronder, kunnen veldtypen niet worden gewijzigd.
  • Velden kunnen op elk moment in een overnamehiërarchie worden toegevoegd of verwijderd.
  • Veld-id's kunnen niet worden gewijzigd.
  • Veld-id's moeten uniek zijn voor elk niveau in een typehiërarchie, maar kunnen worden hergebruikt tussen basisklassen en subklassen. De klasse kan bijvoorbeeld Base een veld met id 0 declareren en een ander veld kan worden gedeclareerd Sub : Base met dezelfde id. 0

Cijfers

  • De ondertekening van een numeriek veld kan niet worden gewijzigd.
    • Conversies tussen int & uint zijn ongeldig.
  • De breedte van een numeriek veld kan worden gewijzigd.
    • Bijvoorbeeld: conversies van int naar long of ulong naar ushort worden ondersteund.
    • Conversies die de breedte beperken, worden gegenereerd als de runtimewaarde van een veld een overloop veroorzaakt.
      • Conversie van ulong naar ushort wordt alleen ondersteund als de waarde tijdens runtime kleiner is dan ushort.MaxValue.
      • Conversies van double naar float worden alleen ondersteund als de runtimewaarde tussen float.MinValue en float.MaxValue.
      • Hetzelfde geldt voor decimal, die een smaller bereik heeft dan beide double en float.

Kopieerapparaten

Orleans bevordert standaard de veiligheid. Dit omvat veiligheid van sommige klassen gelijktijdigheidsfouten. Met name Orleans worden objecten die standaard worden doorgegeven in graanoproepen, onmiddellijk gekopieerd. Dit kopiëren wordt mogelijk gemaakt door Orleans. Serialisatie en wanneer Orleans.CodeGeneration.GenerateSerializerAttribute ze worden toegepast op een type, Orleans genereren ook kopieerders voor dat type. Orleans vermijdt het kopiëren van typen of afzonderlijke leden die zijn gemarkeerd met de ImmutableAttribute. Zie Serialisatie van onveranderbare typen in Orleansvoor meer informatie.

Best practices voor serialisatie

  • Geef uw typen aliassen op met behulp van het [Alias("my-type")] kenmerk. Typen met aliassen kunnen worden hernoemd zonder dat de compatibiliteit wordt onderbroken.

  • Wijzig een record regelmaat class of omgekeerd niet. Records en klassen worden niet op dezelfde manier weergegeven, omdat records naast gewone leden primaire constructorleden hebben en de twee dus niet kunnen worden uitgewisseld.

  • Voeg geen nieuwe typen toe aan een bestaande typehiërarchie voor een serialiseerbaar type. U mag geen nieuwe basisklasse toevoegen aan een bestaand type. U kunt veilig een nieuwe subklasse toevoegen aan een bestaand type.

  • Vervang het gebruik door SerializableAttribute GenerateSerializerAttribute en bijbehorende IdAttribute declaraties.

  • Start alle lid-id's op nul voor elk type. Id's in een subklasse en de bijbehorende basisklasse kunnen veilig overlappen. Beide eigenschappen in het volgende voorbeeld hebben id's die gelijk zijn aan 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; }
    }
    
  • Verbreed numerieke lidtypen indien nodig. U kunt zich breder sbyte maken tot int short long.

    • U kunt numerieke lidtypen beperken, maar dit resulteert in een runtime-uitzondering als waargenomen waarden niet correct kunnen worden weergegeven door het beperkte type. Kan bijvoorbeeld int.MaxValue niet worden vertegenwoordigd door een short veld, zodat het beperken van een int veld dat short kan resulteren in een runtime-uitzondering als een dergelijke waarde is aangetroffen.
  • Wijzig de ondertekening van een lid van het numerieke type niet . U mag bijvoorbeeld het type van een lid niet wijzigen van uint in int of van een int in een uint.

Serializers voor graanopslag

Orleans bevat een door een provider ondersteund persistentiemodel voor korrels, toegankelijk via de State eigenschap of door een of meer IPersistentState<TState> waarden in uw graan te injecteren. Vóór Orleans 7.0 had elke provider een ander mechanisme voor het configureren van serialisatie. In Orleans 7.0 is er nu een serialisatieinterface voor algemene statusstatussen, IGrainStorageSerializerdie een consistente manier biedt om statusserialisatie voor elke provider aan te passen. Ondersteunde opslagproviders implementeren een patroon waarbij de eigenschap wordt ingesteld op de IStorageProviderSerializerOptions.GrainStorageSerializer klasse Opties van de provider, bijvoorbeeld:

Korrelopslagserialisatie wordt momenteel standaard geserialiseerd Newtonsoft.Json . U kunt deze vervangen door die eigenschap te wijzigen tijdens de configuratie. In het volgende voorbeeld ziet u dit met behulp van OptionsBuilder<TOptions>:

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

Zie OptionsBuilder-API voor meer informatie.

Orleans heeft een geavanceerd en uitbreidbaar serialisatieframework. Orleans serialiseert gegevenstypen die worden doorgegeven in graanaanvraag- en antwoordberichten, evenals korrelige permanente statusobjecten. Als onderdeel van dit framework Orleans genereert u automatisch serialisatiecode voor deze gegevenstypen. Naast het genereren van een efficiëntere serialisatie/deserialisatie voor typen die al zijn. NET-serializeerbaar, Orleans probeert ook serialisatieprogramma's te genereren voor typen die niet worden gebruikt in graaninterfaces. NET-serializeerbaar. Het framework bevat ook een set efficiënte ingebouwde serializers voor veelgebruikte typen: lijsten, woordenlijsten, tekenreeksen, primitieven, matrices, enzovoort.

Twee belangrijke kenmerken van de serialisatiefunctie stellen het af van veel andere serialisatieframeworks van Orleansderden: dynamische typen/willekeurige polymorfisme en objectidentiteit.

  1. Dynamische typen en willekeurige polymorfisme: Orleans dwingt geen beperkingen af voor de typen die kunnen worden doorgegeven in graanoproepen en houdt de dynamische aard van het werkelijke gegevenstype bij. Dat betekent bijvoorbeeld dat als de methode in de graaninterfaces wordt gedeclareerd om te accepteren IDictionary , maar tijdens runtime de afzender passeert SortedDictionary<TKey,TValue>, de ontvanger inderdaad krijgt SortedDictionary (hoewel de "statische contract"/grain-interface dit gedrag niet heeft opgegeven).

  2. Objectidentiteit behouden: als hetzelfde object meerdere typen in de argumenten van een aanroep van een graan wordt doorgegeven of indirect meer dan één keer wordt verwezen vanuit de argumenten, Orleans wordt het slechts één keer geserialiseerd. Aan de ontvangerzijde Orleans worden alle verwijzingen correct hersteld, zodat twee aanwijzers naar hetzelfde object nog steeds verwijzen na deserialisatie. Objectidentiteit is belangrijk om te behouden in scenario's zoals de volgende. Stel dat grain A een woordenlijst met 100 vermeldingen naar korrel B verzendt en 10 van de toetsen in de woordenlijst verwijst naar hetzelfde object, obj, aan de zijkant van A. Zonder de objectidentiteit te behouden, ontvangt B een woordenlijst van 100 vermeldingen met die 10 sleutels die verwijzen naar 10 verschillende klonen van obj. Met objectidentiteit behouden, ziet de woordenlijst aan de kant van B er precies zo uit als aan de zijde van A met die 10 sleutels die verwijzen naar één object obj.

De bovenstaande twee gedragingen worden geleverd door de standaard .NET binaire serializer en het was daarom belangrijk voor ons om dit standaard en vertrouwde gedrag ook Orleans te ondersteunen.

Gegenereerde serializers

Orleans gebruikt de volgende regels om te bepalen welke serializers moeten worden gegenereerd. De regels zijn:

  1. Scan alle typen in alle assembly's die verwijzen naar de kernbibliotheek Orleans .
  2. Buiten deze assembly's: serializers genereren voor typen waarnaar rechtstreeks wordt verwezen in methodehandtekeningen van graininterfaces of handtekening van statusklassen of voor elk type dat is gemarkeerd met SerializableAttribute.
  3. Daarnaast kan een graaninterface- of implementatieproject verwijzen naar willekeurige typen voor het genereren van serialisatie door kenmerken KnownTypeAttribute op assemblyniveau KnownAssemblyAttribute toe te voegen om codegenerator te laten weten dat er serialisatieprogramma's moeten worden gegenereerd voor specifieke typen of alle in aanmerking komende typen binnen een assembly. Zie Kenmerken toepassen op het niveau van de assembly voor meer informatie over kenmerken op assemblyniveau.

Serialisatie van terugval

Orleans ondersteunt de overdracht van willekeurige typen tijdens runtime en daarom kan de ingebouwde codegenerator de volledige set typen die van tevoren worden verzonden, niet bepalen. Bovendien kunnen bepaalde typen geen serialisatieprogramma's hebben die voor hen zijn gegenereerd omdat ze niet toegankelijk zijn (bijvoorbeeld private) of niet-toegankelijke velden hebben (bijvoorbeeld readonly). Daarom is er behoefte aan Just-In-Time-serialisatie van typen die onverwacht waren of die vooraf geen serialisatieprogramma's konden hebben. De serializer die verantwoordelijk is voor deze typen wordt de fallback-serializer genoemd. Orleans wordt geleverd met twee terugvalsserialisaties:

De terugvalsserialisatie kan worden geconfigureerd met behulp van de FallbackSerializationProvider eigenschap op zowel ClientConfiguration de client GlobalConfiguration als op de silo's.

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

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

U kunt ook de serialisatieprovider voor terugval opgeven in de XML-configuratie:

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

Dit BinaryFormatterSerializer is de standaard serialisatiefunctie voor terugval.

Waarschuwing

Binaire serialisatie met BinaryFormatter kan gevaarlijk zijn. Zie de beveiligingshandleiding binaryFormatter en de binaryFormatter-migratiehandleiding voor meer informatie.

Serialisatie van uitzonderingen

Uitzonderingen worden geserialiseerd met behulp van de terugvalsserialisatie. Het standaardconfiguratiegebruik BinaryFormatter is de fallback-serializer en daarom moet het ISerializable-patroon worden gevolgd om ervoor te zorgen dat alle eigenschappen in een uitzonderingstype correct worden geserialiseerd.

Hier volgt een voorbeeld van een uitzonderingstype met correct geïmplementeerde serialisatie:

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

Best practices voor serialisatie

Serialisatie dient voor twee primaire doeleinden in Orleans:

  1. Als draadindeling voor het verzenden van gegevens tussen korrels en clients tijdens runtime.
  2. Als opslagindeling voor het behouden van gegevens met een lange levensduur voor later ophalen.

De serializers die door Orleans zijn gegenereerd, zijn geschikt voor het eerste doel vanwege hun flexibiliteit, prestaties en veelzijdigheid. Ze zijn niet zo geschikt voor het tweede doel, omdat ze niet expliciet versietolerant zijn. Het wordt aanbevolen dat gebruikers een versietolerante serializer configureren, zoals ProtocolBuffers voor permanente gegevens. Protocolbuffers worden ondersteund via Orleans.Serialization.ProtobufSerializer microsoft .Orleans. OrleansGoogleUtils NuGet-pakket. De aanbevolen procedures voor de specifieke serializer van de keuze moeten worden gebruikt om versietolerantie te garanderen. Serializers van derden kunnen worden geconfigureerd met behulp van de SerializationProviders configuratie-eigenschap zoals hierboven beschreven.