Skip to content

[Draft] Sample step-by-step descriptions #41993

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ helpviewer_keywords:

The .NET base class libraries provide two XML serializers: [XmlSerializer](../introducing-xml-serialization.md) and [DataContractSerializer](../../../fundamentals/runtime-libraries/system-runtime-serialization-datacontractserializer.md). There are some subtle differences between these two, but for the purpose of the migration, this section focuses only on `DataContractSerializer`. Why? Because it **fully supports the serialization programming model that was used by `BinaryFormatter`**. All the types that are already marked as `[Serializable]` or implement `ISerializable` can be serialized with `DataContractSerializer`. Where is the catch? Known types must be specified up front (that's why it's secure). You need to know them and be able to get the `Type`, **even for private types**.

It's not required to specify most popular collections or primitive types like `string` or `DateTime` (the serializer has its own default allowlist), but there are exceptions like `DateTimeOffset`. For more information about the supported types, see [Types supported by the data contract serializer](../../../framework/wcf/feature-details/types-supported-by-the-data-contract-serializer.md).

[Partial trust](../../../framework/wcf/feature-details/partial-trust.md) is a .NET Framework feature that wasn't ported to .NET (Core). If your code runs on .NET Framework and uses this feature, read about the [limitations](../../../framework/wcf/feature-details/types-supported-by-the-data-contract-serializer.md#limitations-of-using-certain-types-in-partial-trust-mode) that might apply to such a scenario.

## Step by step migration

1. Find all the usages of `BinaryFormatter`.
2. Ensure that the serialization code paths are covered with tests, so you can verify your changes and avoid introducing bugs.
3. You don't need to install any pacakges, as `DataContractSerializer` is part of the .NET shared framework.
4. Find all the types that are being serialized with `BinaryFormatter`. You don't need to modify any of them, but you may need to list them via `knownTypes` argument of the `DataContractSerializer` ctor.
5. Replace the usage of `BinaryFormatter` with `DataContractSerializer`.

```csharp
DataContractSerializer serializer = new(
type: input.GetType(),
Expand All @@ -26,7 +38,3 @@ DataContractSerializer serializer = new(
typeof(MyType2)
});
```

It's not required to specify most popular collections or primitive types like `string` or `DateTime` (the serializer has its own default allowlist), but there are exceptions like `DateTimeOffset`. For more information about the supported types, see [Types supported by the data contract serializer](../../../framework/wcf/feature-details/types-supported-by-the-data-contract-serializer.md).

[Partial trust](../../../framework/wcf/feature-details/partial-trust.md) is a .NET Framework feature that wasn't ported to .NET (Core). If your code runs on .NET Framework and uses this feature, read about the [limitations](../../../framework/wcf/feature-details/types-supported-by-the-data-contract-serializer.md#limitations-of-using-certain-types-in-partial-trust-mode) that might apply to such a scenario.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,42 @@ helpviewer_keywords:

- By default, both public and non-public types are serializable. Every type needs to provide a parameterless constructor.
- protobuf-net requires each serializable type to be annotated with `[ProtoContract]` attribute.
- Every serializable non-static field and a property needs to be annotated with `[ProtoMember(int tag)]` attribute. The member names aren't encoded in the data. Instead, the users must pick an integer to identify each member.
- Every serializable non-static field and a property needs to be annotated with `[ProtoMember(int identifier)]` attribute. The member names aren't encoded in the data. Instead, the users must pick an integer to identify each member.
- [Inheritance](https://github.com/protobuf-net/protobuf-net?tab=readme-ov-file#inheritance) must be explicitly declared via `[ProtoInclude(...)]` attribute on each type with known subtypes.
- Read-only fields are supported by default.

## Step by step migration

1. Find all the usages of `BinaryFormatter`.
2. Ensure that the serialization code paths are covered with tests, so you can verify your changes and avoid introducing bugs.
3. Install `protobuf-net` package.
4. Find all the types that are being serialized with `BinaryFormatter`.
5. For types that you can modify:
- Annotate with `[ProtoContract]` attribute all types that are marked with `[Serializable]` or implement the `ISerializable` interface. If these types are not exposed to other apps (example: you are writing a library) that may use different serializers like `DataContractSerializer`, you can remove the `[Serializable]` and `ISerializable` annotations.
- For derived types, apply `[ProtoInclude(...)]` to their base types (see the example below).
- For every type that declares any constructor that accepts parameters, add a parameterless constructor. Leave a comment that explains the protobuf-net requirement (so nobody removes it by accident).
- Mark all the members (fields and properties) that you wish to serialize with `[ProtoMember(int identifier)]`. All identifiers must be unique within a single type, but the same numbers can be re-used in sub-types if inheritance is enabled.
6. For types that you can't modify:
- For types provided by the .NET itself, you can use `ProtoBuf.Meta.RuntimeTypeModel.Default.CanSerialize(Type type)` API to check if they are natively supported by protobuf-net.
- You can create dedicated data transfer objects (DTO) and map them accordingly (you could use implicit cast operator for that).
- Use the `RuntimeTypeModel` API to define everything that the attributes allow for.
7. Replace the usage of `BinaryFormatter` with `ProtoBuf.Serializer`.

```diff
-[Serializable]
+[ProtoContract]
+[ProtoInclude(2, typeof(Point2D))]
public class Point1D
{
+ [ProtoMember(1)]
public int X { get; set; }
}

-[Serializable]
+[ProtoContract]
public class Point2D : Point1D
{
+ [ProtoMember(2)]
public int Y { get; set; }
}
```
Loading