다음을 통해 공유


Orleans의 serialization

Orleans에는 크게 두 가지 종류의 serialization이 사용됩니다.

  • 조직 호출 serialization - 조직과 주고받는 개체를 직렬화하는 데 사용됩니다.
  • 조직 스토리지 serialization - 스토리지 시스템과 개체를 직렬화하는 데 사용됩니다.

이 문서의 대부분은 Orleans에 포함된 serialization 프레임워크를 통한 조직 호출 serialization에 관한 것입니다. 조직 스토리지 직렬 변환기 섹션에서는 조직 스토리지 serialization에 대해 설명합니다.

Orleans serialization 사용

Orleans에는 Orleans.Serialization이라고 할 수 있는 확장 가능한 고급 serialization 프레임워크가 포함되어 있습니다. Orleans에 포함된 serialization 프레임워크는 다음 목표를 충족하도록 설계되었습니다.

  • 고성능 - 직렬 변환기는 성능에 맞게 디자인 및 최적화되었습니다. 자세한 내용은 이 프레젠테이션에서 확인할 수 있습니다.
  • 높은 충실도 - 직렬 변환기는 제네릭, 다형성, 상속 계층 구조, 개체 ID 및 순환 그래프에 대한 지원을 포함하여 .NET 형식 시스템의 대부분을 충실하게 나타냅니다. 포인터는 프로세스 간에 이식할 수 없으므로 지원되지 않습니다.
  • 유연성 - 직렬 변환기는 서로게이트를 만들거나 System.Text.Json, Newtonsoft.JsonGoogle.Protobuf와 같은 외부 serialization 라이브러리에 위임하여 타사 라이브러리를 지원하도록 사용자 지정할 수 있습니다.
  • 버전 허용 범위 - 직렬 변환기를 사용하면 시간이 지남에 따라 애플리케이션 형식이 발전하여 다음을 지원할 수 있습니다.
    • 멤버 추가 및 제거
    • 하위 클래스
    • 숫자 확대 및 축소(예: int에서 long까지, float에서 double까지)
    • 형식 이름 바꾸기

높은 충실도의 형식 표현은 직렬 변환기에서는 흔하지 않으므로 몇 가지 사항은 추가 설명이 필요합니다.

  1. 동적 형식 및 임의 다형성: Orleans는 조직 호출에서 전달할 수 있는 형식에 제한을 두지 않고 실제 데이터 형식의 동적 특성을 유지합니다. 즉, 예를 들어 조직 인터페이스의 메서드가 IDictionary를 수락하도록 선언되었지만 런타임에 발신자가 SortedDictionary<TKey,TValue>를 전달하는 경우 수신자는 실제로 SortedDictionary를 가져옵니다("정적 계약"/조직 인터페이스가 이 동작을 지정하지 않았음에도 불구하고).

  2. 개체 ID 유지: 동일한 개체가 조직 호출의 인수에서 여러 형식으로 전달되거나 인수에서 간접적으로 두 번 이상 가리키는 경우 Orleans는 한 번만 직렬화합니다. 수신기 쪽에서 Orleans는 모든 참조를 올바르게 복원하여 동일한 개체에 대한 두 포인터가 deserialization 후에도 동일한 개체를 가리키도록 합니다. 개체 ID는 다음과 같은 시나리오에서 유지하는 데 중요합니다. 조직 A가 100개의 항목이 포함된 사전을 조직 B로 보내고 사전의 키 중 10개가 A 쪽의 동일한 개체 obj를 가리킨다고 가정해 보겠습니다. 개체 ID를 유지하지 않으면 B는 obj의 10개의 서로 다른 복제본을 가리키는 10개의 키가 포함된 100개 엔터티의 사전을 받게 됩니다. 개체 ID가 보존된 경우 B 측의 사전은 단일 개체 obj를 가리키는 10개의 키가 있는 A 측의 사전과 똑같아 보입니다. .NET의 기본 문자열 해시 코드 구현은 프로세스별로 임의로 지정되므로 사전 및 해시 집합(예:)의 값 순서가 유지되지 않을 수 있습니다.

버전 허용 범위를 지원하기 위해 직렬 변환기에서는 개발자가 직렬화되는 형식과 멤버를 명시적으로 지정해야 합니다. 이 작업을 가능한 한 수월하게 만들기 위해 노력했습니다. 모든 직렬화 가능 형식을 Orleans.GenerateSerializerAttribute로 표시하여 Orleans에 해당 형식에 대한 직렬 변환기 코드를 생성하도록 지시해야 합니다. 이 작업을 수행한 후에는 여기에 설명된 대로 포함된 코드 수정을 사용하여 형식의 직렬화 가능 멤버에 필요한 Orleans.IdAttribute를 추가할 수 있습니다.

포함 형식에 해당 멤버에 대한 IdAttribute가 포함되지 않은 경우 GenerateSerializerAttribute에 제안 및 적용되는 사용 가능한 코드 수정을 애니메이션으로 보여주는 이미지.

다음은 특성을 적용하는 방법을 보여 주는 Orleans의 직렬화 가능 형식의 예입니다.

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

Orleans는 상속을 지원하고 계층 구조의 개별 계층을 개별적으로 직렬화하여 고유한 멤버 ID를 가질 수 있도록 합니다.

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

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

앞의 코드에서 BookPublication에서 파생되었음에도 불구하고 PublicationBook 모두 [Id(0)]을 포함하는 멤버를 가지고 있습니다. 멤버 식별자의 범위는 전체 형식이 아닌 상속 수준으로 지정되므로 이는 Orleans에서 권장되는 방법입니다. 멤버는 PublicationBook에서 독립적으로 추가 및 제거될 수 있지만 애플리케이션이 배포된 후에는 특별한 고려 없이 새 기본 클래스를 계층 구조에 삽입할 수 없습니다.

또한 Orleans는 다음 형식 예와 같이 internal, privatereadonly 멤버를 사용하여 형식 직렬화를 지원합니다.

[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는 전체 이름을 인코딩하여 형식을 직렬화합니다. Orleans.AliasAttribute를 추가하여 이 설정을 재정의할 수 있습니다. 이렇게 하면 기본 클래스의 이름을 바꾸거나 어셈블리 간에 이동하는 데 복원력 있는 이름을 사용하여 형식이 직렬화됩니다. 형식 별칭은 전역적으로 범위가 지정되며 애플리케이션에서 동일한 값을 가진 두 개의 별칭을 가질 수 없습니다. 제네릭 형식의 경우 별칭 값에는 백틱 앞에 오는 제네릭 매개 변수 수가 포함되어야 합니다. 예를 들어 MyGenericType<T, U>에는 별칭 [Alias("mytype`2")]가 있을 수 있습니다.

record 형식 직렬화 중

레코드의 기본 생성자에 정의된 멤버에는 기본적으로 암시적 ID가 있습니다. 즉, Orleans는 record 형식 직렬화를 지원합니다. 즉, 이미 배포된 형식의 매개 변수 순서를 변경할 수 없습니다. 이전 버전의 애플리케이션(롤링 업그레이드의 경우)과 스토리지 및 스트림에서 해당 형식의 직렬화된 인스턴스와의 호환성이 손상되기 때문입니다. 레코드 형식의 본문에 정의된 멤버는 ID를 기본 생성자 매개 변수와 공유하지 않습니다.

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

기본 생성자 매개 변수가 직렬화 가능 필드로 자동 포함되는 것을 원하지 않으면 [GenerateSerializer(IncludePrimaryConstructorParameters = false)]를 사용할 수 있습니다.

외래 형식을 직렬화하기 위한 서로게이트

경우에 따라 완전히 제어할 수 없는 조직 간에 형식을 전달해야 할 수 있습니다. 이러한 경우 수동으로 애플리케이션 코드를 일부 사용자 지정 정의 형식으로 또는 그 반대로 변환하는 것은 실용적이지 않을 수 있습니다. Orleans는 서로게이트 형식의 형태로 이러한 상황에 대한 솔루션을 제공합니다. 서로게이트는 대상 형식 대신 직렬화되며 대상 형식으로 또는 대상 형식에서 변환될 수 있습니다. 외래 형식 및 해당 서로게이트 및 변환기에 대한 다음 예제를 생각해 보겠습니다.

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

위의 코드에서

  • MyForeignLibraryValueType는 사용하는 라이브러리에 정의된 컨트롤 외부의 형식입니다.
  • MyForeignLibraryValueTypeSurrogateMyForeignLibraryValueType에 매핑되는 서로게이트 형식입니다.
  • RegisterConverterAttributeMyForeignLibraryValueTypeSurrogateConverter가 두 형식 간에 매핑할 변환기 역할을 하도록 지정합니다. 이 클래스는 IConverter<TValue,TSurrogate> 인터페이스의 구현입니다.

Orleans는 형식 계층 구조(다른 형식에서 파생되는 형식)의 형식 serialization을 지원합니다. 형식 계층 구조에 외래 형식이 나타날 수 있는 경우(예: 사용자 고유의 형식 중 하나에 대한 기본 클래스로) Orleans.IPopulator<TValue,TSurrogate> 인터페이스를 추가로 구현해야 합니다. 다음과 같은 예제를 참조하세요.

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

버전 관리 규칙

개발자가 형식을 수정할 때 일련의 규칙을 따르는 경우 버전 허용 범위가 지원됩니다. 개발자가 Google 프로토콜 버퍼(Protobuf)와 같은 시스템에 익숙하다면 이러한 규칙도 익숙할 것입니다.

복합 형식(classstruct)

  • 상속은 지원되지만 개체의 상속 계층 구조를 수정하는 것은 지원되지 않습니다. 클래스의 기본 클래스를 추가하거나 다른 클래스로 변경하거나 제거할 수 없습니다.
  • 아래 숫자 섹션에 설명된 일부 숫자 형식을 제외하고 필드 형식은 변경할 수 없습니다.
  • 필드는 상속 계층 구조의 모든 지점에서 추가하거나 제거할 수 있습니다.
  • 필드 ID는 변경할 수 없습니다.
  • 필드 ID는 형식 계층 구조의 각 수준마다 고유해야 하지만 기본 클래스와 하위 클래스 간에 재사용할 수 있습니다. 예를 들어, Base 클래스는 ID가 0인 필드를 선언할 수 있고 동일한 ID인 0을 사용하여 Sub : Base에서 다른 필드를 선언할 수 있습니다.

숫자

  • 숫자 필드의 부호가 있는지 여부를 변경할 수 없습니다.
    • intuint 사이의 변환이 유효하지 않습니다.
  • 숫자 필드의 너비는 변경할 수 있습니다.
    • 예: int에서 long으로 또는 ulong에서 ushort로의 변환이 지원됩니다.
    • 필드의 런타임 값으로 인해 오버플로가 발생하는 경우 너비를 줄이는 변환이 throw됩니다.
      • ulong에서 ushort로의 변환은 런타임 값이 ushort.MaxValue보다 작은 경우에만 지원됩니다.
      • double에서 float로의 변환은 런타임 값이 float.MinValue에서 float.MaxValue 사이인 경우에만 지원됩니다.
      • doublefloat보다 범위가 더 좁은 decimal의 경우에도 마찬가지입니다.

복사기

Orleans는 기본적으로 안전의 수준을 올립니다. 여기에는 일부 동시성 버그 클래스로부터의 안전이 포함됩니다. 특히, Orleans는 기본적으로 조직 호출에 전달된 개체를 즉시 복사합니다. 이 복사는 Orleans.Serialization에 의해 촉진되고, Orleans.CodeGeneration.GenerateSerializerAttribute가 형식에 적용되면 Orleans는 해당 형식에 대한 복사기도 생성합니다. Orleans는 ImmutableAttribute를 사용하여 표시된 형식이나 개별 멤버를 복사하지 않습니다. 자세한 내용은 Orleans에서 변경이 불가능한 형식의 serialization을 참조하세요.

Serialization 모범 사례

  • [Alias("my-type")] 특성을 사용하여 형식 별칭을 지정하세요. 별칭이 있는 형식의 이름은 호환성을 손상하지 않고 바꿀 수 있습니다.

  • record를 일반 class로 또는 그 반대로 변경하지 마세요. 레코드에는 일반 멤버 외에도 기본 생성자 멤버가 있어 레코드와 클래스가 동일한 방식으로 표현되지 않으므로 둘을 바꿔 사용할 수 없습니다.

  • ❌직렬화 가능한 형식에 대한 기존 형식 계층 구조에 새 형식을 추가하지 마세요. 기존 형식에 새 Base 클래스를 추가하면 안 됩니다. 기존 형식에 새 하위 클래스를 안전하게 추가할 수 있습니다.

  • SerializableAttribute의 사용량을 GenerateSerializerAttribute 및 해당 IdAttribute 선언으로 바꾸세요.

  • ✅각 형식에 대해 모든 멤버 ID를 0으로 시작하세요. 하위 클래스의 ID와 해당 기본 클래스의 ID는 중복되어도 안전합니다. 다음 예제의 두 속성은 모두 0라는 ID를 갖습니다.

    [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; }
    }
    
  • ✅필요에 따라 숫자 멤버 형식을 확장하세요. sbyteshort, int, long로 확장할 수 있습니다.

    • 숫자 멤버 형식의 범위를 좁힐 수 있지만 관찰된 값을 좁은 형식으로 올바르게 나타낼 수 없는 경우 런타임 예외가 발생합니다. 예를 들어 int.MaxValueshort 필드로 나타낼 수 없으므로 int 필드를 short로 좁히면 이러한 값이 발생한 경우 런타임 예외가 발생할 수 있습니다.
  • ❌숫자 형식 멤버의 부호를 변경하지 마세요. 예를 들어 멤버의 형식을 uint에서 int로 또는 int에서 uint으로 변경해서는 안 됩니다.

조직 스토리지 직렬 변환기

Orleans에는 State 속성을 통해 또는 조직에 하나 이상의 IPersistentState<TState> 값을 주입하여 액세스하는 조직에 대한 공급자 기반 지속성 모델이 포함되어 있습니다. Orleans 7.0 이전에는 각 공급자마다 serialization 구성을 위한 서로 다른 메커니즘을 사용했습니다. Orleans 7.0에서는 이제 각 공급자에 대한 상태 serialization을 사용자 지정하는 일관된 방법을 제공하는 범용 조직 상태 직렬 변환기 인터페이스 IGrainStorageSerializer가 있습니다. 지원되는 스토리지 공급자는 공급자의 옵션 클래스에서 IStorageProviderSerializerOptions.GrainStorageSerializer 속성을 설정하는 패턴을 구현합니다. 예를 들면 다음과 같습니다.

조직 스토리지 직렬화는 현재 기본적으로 Newtonsoft.Json을 사용하여 상태를 직렬화합니다. 구성 시 이 속성을 수정할 수 있습니다. 다음 예제에서는 이를 위해 OptionsBuilder<TOptions>를 사용하는 방법을 보여 줍니다.

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

자세한 내용은 OptionsBuilder API를 참조하세요.

Orleans에는 확장 가능한 고급 serialization 프레임워크가 있습니다. Orleans는 조직 요청 및 응답 메시지와 조직 영구 상태 개체에 전달된 데이터 형식을 직렬화합니다. 이 프레임워크의 일부로 Orleans는 해당 데이터 형식에 대한 serialization 코드를 자동으로 생성합니다. 이미 .NET 직렬화 가능한 유형에 대해 보다 효율적인 serialization/deserialization을 생성하는 것 외에도. Orleans는 .NET 직렬화가 불가능한 조직 인터페이스에 사용되는 유형에 대한 직렬 변화기도 생성하려고 합니다. 프레임워크에는 목록, 사전, 문자열, 기본 형식, 배열 등 자주 사용되는 유형에 대한 효율적인 기본 제공 직렬 변환기 세트도 포함되어 있습니다.

Orleans 직렬 변환기의 두 가지 중요한 기능은 동적 형식/임의 다형성 및 개체 ID라는 다른 많은 타사 serialization 프레임워크와 차별화됩니다.

  1. 동적 형식 및 임의 다형성: Orleans는 조직 호출에서 전달할 수 있는 형식에 제한을 두지 않고 실제 데이터 형식의 동적 특성을 유지합니다. 즉, 예를 들어 조직 인터페이스의 메서드가 IDictionary를 수락하도록 선언되었지만 런타임에 발신자가 SortedDictionary<TKey,TValue>를 전달하는 경우 수신자는 실제로 SortedDictionary를 가져옵니다("정적 계약"/조직 인터페이스가 이 동작을 지정하지 않았음에도 불구하고).

  2. 개체 ID 유지: 동일한 개체가 조직 호출의 인수에서 여러 형식으로 전달되거나 인수에서 간접적으로 두 번 이상 가리키는 경우 Orleans는 한 번만 직렬화합니다. 수신기 쪽에서 Orleans는 모든 참조를 올바르게 복원하여 동일한 개체에 대한 두 포인터가 deserialization 후에도 동일한 개체를 가리키도록 합니다. 개체 ID는 다음과 같은 시나리오에서 유지하는 데 중요합니다. 조직 A가 100개의 항목이 포함된 사전을 조직 B로 보내고 사전의 키 중 10개가 A 쪽의 동일한 개체 obj를 가리킨다고 가정해 보겠습니다. 개체 ID를 유지하지 않으면 B는 10개의 다른 obj 클론을 가리키는 10개의 키가 있는 100개 항목의 사전을 받게 됩니다. 개체 ID가 유지되면 B 측의 사전은 단일 개체 obj를 가리키는 10개의 키가 있는 A 쪽과 똑같이 보입니다.

위의 두 동작은 표준 .NET 이진 직렬 변환기에서 제공되므로 Orleans에서도 이 표준 및 친숙한 동작을 지원하는 것이 중요했습니다.

생성된 직렬 변환기

Orleans는 다음 규칙을 사용하여 생성할 직렬 변환기를 결정합니다. 규칙은 다음과 같습니다.

  1. 핵심 Orleans 라이브러리를 참조하는 모든 어셈블리의 모든 형식을 검사합니다.
  2. 이러한 어셈블리에서: 조직 인터페이스 메서드 시그니처 또는 상태 클래스 시그니처에서 직접 참조되는 형식 또는 SerializableAttribute로 표시된 모든 형식에 대해 직렬 변환기를 생성합니다.
  3. 또한 조직 인터페이스 또는 구현 프로젝트는 KnownTypeAttribute 또는 KnownAssemblyAttribute 어셈블리 수준 속성을 추가하여 어셈블리 내의 특정 유형 또는 모든 적격 형식에 대한 직렬 변환기를 생성하도록 코드 생성기에 지시함으로써 serialization 생성을 위한 임의 형식을 가리킬 수 있습니다. 어셈블리 수준 특성에 대한 자세한 내용은 어셈블리 수준에서 특성 적용을 참조하세요.

대체 serialization

Orleans는 런타임에 임의 형식의 전송을 지원하므로 기본 제공 코드 생성기는 미리 전송될 전체 형식 세트를 확인할 수 없습니다. 또한 특정 형식은 액세스할 수 없거나(예: private) 액세스할 수 없는 필드(예: readonly)가 있기 때문에 직렬 변환기를 생성할 수 없습니다. 따라서 예기치 않았거나 미리 직렬 변환기를 생성할 수 없는 형식의 Just-In-Time serialization이 필요합니다. 이러한 형식을 담당하는 직렬 변환기를 대체 직렬 변환기라고 합니다. Orleans는 두 개의 대체 직렬 변환기와 함께 제공됩니다.

대체 직렬 변환기는 클라이언트의 ClientConfiguration과 사일로의 GlobalConfiguration 모두에서 FallbackSerializationProvider 속성을 사용하여 구성할 수 있습니다.

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

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

또는 대체 serialization 공급자를 XML 구성에서 지정할 수 있습니다.

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

BinaryFormatterSerializer는 기본 대체 직렬 변환기입니다.

Warning

BinaryFormatter를 사용한 이진 직렬화는 위험할 수 있습니다. 자세한 내용은 BinaryFormatter 보안 가이드BinaryFormatter 마이그레이션 가이드를 참조하세요.

예외 serialization

대체 직렬 변환기를 사용하여 예외가 직렬화됩니다. 기본 구성을 사용하는 BinaryFormatter는 대체 직렬 변환기이므로 예외 형식의 모든 속성을 올바르게 직렬화하려면 ISerializable 패턴을 따라야 합니다.

다음은 올바르게 구현된 serialization을 사용하는 예외 형식의 예입니다.

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

Serialization 모범 사례

serialization은 Orleans에서 두 가지 주요 목적으로 사용됩니다.

  1. 런타임에 조직과 클라이언트 간에 데이터를 전송하기 위한 유선 형식입니다.
  2. 나중에 검색할 수 있는 수명이 긴 데이터를 유지하기 위한 스토리지 형식입니다.

Orleans에서 생성한 직렬 변환기는 유연성, 성능 및 다양성으로 인해 첫 번째 목적에 적합합니다. 명시적으로 버전을 허용하지 않기 때문에 두 번째 목적에는 적합하지 않습니다. 사용자는 영구 데이터에 대한 프로토콜 버퍼와 같은 버전 허용 직렬 변환기를 구성하는 것이 좋습니다. 프로토콜 버퍼는 Microsoft.Orleans.OrleansGoogleUtils NuGet 패키지의 Orleans.Serialization.ProtobufSerializer을(를) 통해 지원됩니다. 선택한 특정 직렬 변환기에 대한 모범 사례를 사용하여 버전 허용 범위를 확인해야 합니다. 위에서 설명한 대로 SerializationProviders 구성 속성을 사용하여 타사 직렬 변환기를 구성할 수 있습니다.