Skip to content
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

BinaryFormatter Migration Guide #41564

Merged
merged 65 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
e8dd236
Introduce the outline of a BinaryFormatter migration guide
jeffhandley Apr 24, 2024
8465499
Update NRBF Decoder description
adamsitnik Jun 24, 2024
2c86e69
finish the docs for what we are going to ship in Preview 6:
adamsitnik Jun 26, 2024
c8072fb
fix the build warnings
adamsitnik Jun 26, 2024
21eb44d
add missing file extensions to the relative links (they work anyway, …
adamsitnik Jun 26, 2024
b8d5a3e
fix build errors
adamsitnik Jun 26, 2024
805d7b2
Apply suggestions from code review
adamsitnik Jun 27, 2024
5185093
address code review feedback
adamsitnik Jun 27, 2024
66bedc3
add protobuf-net
adamsitnik Jun 27, 2024
a504238
fix the character
adamsitnik Jun 27, 2024
fe0a882
Apply suggestions from code review
adamsitnik Jul 26, 2024
fd2c330
introducde dedicated migration sections for each described serializer
adamsitnik Jul 26, 2024
b0acc01
remove dev_langs from pages where there are no code samples
adamsitnik Jul 26, 2024
c04d901
rename "choosing" -> "choose" and "reading" -> "read"
adamsitnik Jul 31, 2024
f793dcc
initial WinForms guide
lonitra Jul 31, 2024
4e76345
winforms: fix title and lint issues
lonitra Jul 31, 2024
0faf20f
add step-by-step descriptions
adamsitnik Aug 1, 2024
7d8e7a1
fix the build errors
adamsitnik Aug 1, 2024
3f471f3
address feedback
lonitra Aug 1, 2024
2b06beb
fix url
lonitra Aug 1, 2024
484fa6e
address feedback
lonitra Aug 2, 2024
71729aa
Apply suggestions from code review
lonitra Aug 2, 2024
fa00b2f
Merge branch 'main' into binaryformatter-migration-guide
jeffhandley Aug 3, 2024
d3073ec
Rename files to remove 'overview' from URL and dashes from compound n…
jeffhandley Aug 3, 2024
da8c92b
Improve titles/descriptions; rename files to match. Address feedback.…
jeffhandley Aug 3, 2024
eec25a0
Merge branch 'binaryformatter-migration-guide' into migrationStepByStep
jeffhandley Aug 3, 2024
67aae86
Add beginning of step-by-step migration descriptions
jeffhandley Aug 3, 2024
2e3db58
Apply feedback, including rearranging some content. Author more prose…
jeffhandley Aug 3, 2024
1d6991b
Update some wording, formatting, and titles
jeffhandley Aug 3, 2024
00ddf41
Update MessagePack migration docs
AArnott Aug 3, 2024
57019bc
Fix broken links
adamsitnik Aug 5, 2024
f81998b
protobuf-net additions
mgravell Aug 5, 2024
098ec57
Add WPF migration guide
rchauhan18 Aug 5, 2024
2f228e6
address feedback
lonitra Aug 5, 2024
4b5ebff
Reword introduction and introduce [!CAUTION] sections
terrajobst Aug 5, 2024
2b51171
winforms: add loading resources during runtime section
lonitra Aug 5, 2024
8e64322
Merge branch 'binaryformatter-migration-guide' of https://github.com/…
rchauhan18 Aug 6, 2024
24fdc35
Adding short and ushort to primitive list
rchauhan18 Aug 6, 2024
e4dfa48
winforms/wpf: initial clipboard guidance
lonitra Aug 6, 2024
161fecb
winforms/wpf: fix wpf name
lonitra Aug 6, 2024
2a0193b
Link to BinaryFormatter security guide
terrajobst Aug 6, 2024
fe8cfa8
Change warning to caution and link to migration guide
terrajobst Aug 6, 2024
3daa13f
Add an intro to the migration section
terrajobst Aug 6, 2024
06e5575
winforms/wpf: add BinaryFormatter usage in drag/drop
lonitra Aug 6, 2024
7667e96
Apply suggestions from code review
terrajobst Aug 7, 2024
96e6861
Apply suggestions from Genevieve
terrajobst Aug 7, 2024
7118ca7
Link migration of managed resources from index.md
terrajobst Aug 7, 2024
09bd441
Clarify why DCS is easiest to migrate to
terrajobst Aug 7, 2024
4d854c4
Address feedback and take another pass over the content to make impro…
jeffhandley Aug 7, 2024
c874cb2
Merge branch 'binaryformatter-migration-guide' of https://github.com/…
jeffhandley Aug 7, 2024
b129893
Reword SerializationBinder note per suggestion
jeffhandley Aug 7, 2024
2cea254
Address feedback on intro
jeffhandley Aug 7, 2024
0d717e9
Apply feedback to WPF and WinForms pages
jeffhandley Aug 7, 2024
44a4c42
Trim trailing whitespace
jeffhandley Aug 7, 2024
175b87d
Merge branch 'binaryformatter-migration-guide' of https://github.com/…
jeffhandley Aug 7, 2024
7f986d5
Fix lint whitespace errors
jeffhandley Aug 7, 2024
9a3eec6
Apply feedback: "decision on" --> "reason for"
jeffhandley Aug 7, 2024
caf3729
Apply feedback for C# auto properties and "drag-and-drop" usage
jeffhandley Aug 7, 2024
9506364
Fix broken links
jeffhandley Aug 7, 2024
6c55a0a
Add ref to common OLE scenario and update Journaling
rchauhan18 Aug 7, 2024
d1e6060
Apply suggestions from code review
adamsitnik Aug 7, 2024
406050a
Apply suggestions from code review
lonitra Aug 7, 2024
c0ee74a
Blurbs for submitting issues in the 3 repos
jeffhandley Aug 7, 2024
41f2d34
Link to the migration guide from the BinaryFormatter Security Guide
jeffhandley Aug 7, 2024
fc82129
Fix link
terrajobst Aug 8, 2024
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
24 changes: 24 additions & 0 deletions docs/fundamentals/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,30 @@ items:
href: ../standard/serialization/xml-schema-definition-tool-xsd-exe.md
- name: Binary serialization
items:
- name: BinaryFormatter migration guide
items:
- name: Overview
href: ../standard/serialization/binaryformatter-migration-guide/overview.md
- name: Choose a serializer
href: ../standard/serialization/binaryformatter-migration-guide/choose-a-serializer.md
- name: Migration to DataContractSerializer
href: ../standard/serialization/binaryformatter-migration-guide/migration-to-data-contract-serializer.md
- name: Migration to System.Text.Json
href: ../standard/serialization/binaryformatter-migration-guide/migration-to-system-text-json.md
- name: Migration to MessagePack
href: ../standard/serialization/binaryformatter-migration-guide/migration-to-message-pack.md
- name: Migration to protobuf-net
href: ../standard/serialization/binaryformatter-migration-guide/migration-to-protobuf-net.md
- name: Read NRBF payloads
href: ../standard/serialization/binaryformatter-migration-guide/read-nrbf-payloads.md
- name: Compatibility package
href: ../standard/serialization/binaryformatter-migration-guide/compatibility-package.md
- name: BinaryFormatter functionality reference
href: ../standard/serialization/binaryformatter-migration-guide/functionality-reference.md
- name: WinForms Migration Guide
lonitra marked this conversation as resolved.
Show resolved Hide resolved
href: ../standard/serialization/binaryformatter-migration-guide/winforms-migration-guide.md
- name: WinForms/WPF Clipboard and Drag/Drop Guidance
lonitra marked this conversation as resolved.
Show resolved Hide resolved
href: ../standard/serialization/binaryformatter-migration-guide/winforms-wpf-ole-guidance.md
- name: BinaryFormatter security guide
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
href: ../standard/serialization/binaryformatter-security-guide.md
- name: BinaryFormatter event source
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: "BinaryFormatter Migration Guide: Choose a serializer"
description: "Compare the capabilities and trade-offs of serializers to choose a replacement for BinaryFormatter."
ms.date: 5/31/2024
no-loc: [BinaryFormatter, Serialization]
helpviewer_keywords:
- "BinaryFormatter"
- "serializing objects"
- "serialization"
- "objects, serializing"
---

# Choose a serializer
terrajobst marked this conversation as resolved.
Show resolved Hide resolved

Choosing a serializer format boils down to two questions:

- Is compact binary representation important for your scenario? If so, you need to switch to a different binary serializer. .NET open-source ecosystem provides many great binary serializers and we recommend two of them: [MessagePack](./migration-to-message-pack.md) and [protobuf-net](./migration-to-protobuf-net.md). If compact binary representation is not a must have, you can consider using JSON and XML serialization. For XML, we recommend [DataContractSerializer](./migration-to-data-contract-serializer.md) and for JSON the [System.Text.Json](./migration-to-system-text-json.md).
- Can you modify the types that are being serialized by annotating them with attributes, adding new constructors, making the types public and changing fields to properties? If not, using the modern serializers might require more work (like implementing custom converters or resolvers).

| Feature | BinaryFormatter | DataContractSerializer | System.Text.Json | MessagePack | protobuf-net |
|------------------------------------------------|-----------------|------------------------|-------------------------|--------------------------|-----------------------------------|
| Compact binary representation | ✔️ | ❌ | ❌ | ✔️ | ✔️ |
| Human readable | ❌️ | ✔️ | ✔️ | ❌️ | ❌️ |
| Performance | ❌️ | ❌ | ✔️ | ✔️ | ✔️ |
| `[Serializable]` support | ✔️ | ✔️ | ❌ | ❌ | ❌ |
| Serializing public types | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| Serializing non-public types | ✔️ | ✔️ | ✔️ | ✔️ (resolver required) | ✔️ |
| Serializing fields | ✔️ | ✔️ | ✔️ (opt in) | ✔️ (attribute required) | ✔️ (attribute required) |
| Serializing non-public fields | ✔️ | ✔️ | ✔️ (resolver required) | ✔️ (resolver required) | ✔️ (attribute required) |
| Serializing properties | ✔️<sup>*</sup> | ✔️ | ✔️ | ✔️ (attribute required) | ✔️ (attribute required) |
| Deserializing readonly members | ✔️ | ✔️ | ✔️ (attribute required) | ✔️ | ✔️ (parameterless ctor required) |
| Polymorphic type hierarchy | ✔️ | ✔️ | ✔️ (attribute required) | ✔️ (attribute required) | ✔️ (attribute required) |
| AOT support | ❌️ | ❌ | ✔️ | ✔️ | ❌ (planned) |
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: "BinaryFormatter Migration Guide: Compatibility Package"
description: "Using the BinaryFormatter compatibility package."
ms.date: 5/31/2024
no-loc: [BinaryFormatter, Serialization]
helpviewer_keywords:
- "BinaryFormatter"
- "serializing objects"
- "serialization"
- "objects, serializing"
---

# Using the compatibility package

> [!WARNING]
> The compatibility package is unsafe and not recommended.

.NET 9+ users who can't migrate away from `BinaryFormatter` can install the [System.Runtime.Serialization.Formatters](https://www.nuget.org/packages/System.Runtime.Serialization.Formatters) NuGet package and set the `System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization` AppContext switch to `true`.

```xml
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="9.0.0-*" />
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>
```

> [!WARNING]
> The compatibility package is unsafe and not recommended.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
title: "BinaryFormatter Migration Guide: Functionality Reference"
description: "A reference for BinaryFormatter's functionality that may need to be considered during migrations."
ms.date: 5/31/2024
no-loc: [BinaryFormatter, Serialization, WinForms]
dev_langs:
- CSharp
helpviewer_keywords:
- "BinaryFormatter"
- "serializing objects"
- "serialization"
- "objects, serializing"
---

# BinaryFormatter functionality reference

The `BinaryFormatter` was first introduced with the initial release of the .NET Framework in 2002. To understand how to replace usage of BinaryFormatter, it helps to know how BinaryFormatter works.

`BinaryFormatter` can serialize any instance of any type that's annotated with `[Serializable]` or implements the `ISerializable` interface.

## Member names

In most common scenario, the type is annotated with `[Serializable]` and the serializer uses reflection to serialize **all fields** (both public and non-public) except those that are annotated with `[NonSerialized]`.
For C# auto properties (`{ get; set; }`), the properties are backed by fields generated by the C# compiler, so the names of the serialized fields are compiler generated (and not very human friendly).
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved

A C# decompiler (such as [https://sharplab.io/](https://sharplab.io/) or [ILSpy](https://github.com/icsharpcode/ILSpy)) can demonstrate how C# auto properties are presented to the runtime.

```csharp
[Serializable]
internal class PropertySample
{
public string Name { get; set; }
}
```

The previous class is translated by the C# compiler to:

```csharp
[Serializable]
internal class PropertySample
{
private string <Name>k__BackingField;

public string Name
{
get
{
return <Name>k__BackingField;
}
set
{
<Name>k__BackingField = value;
}
}
}
```

In this case, `<Name>k__BackingField` is **the name of the member that `BinaryFormatter` uses in the serialized payload**. It's not possible to use `nameof` or any other C# operator to get this name.

The [ISerializable](/dotnet/api/system.runtime.serialization.iserializable) interface comes with [GetObjectData(SerializationInfo info, StreamingContext context)](/dotnet/api/system.runtime.serialization.iserializable.getobjectdata) method that allows the users to control the names, by using one of the [SerializationInfo.AddValue](/dotnet/api/system.runtime.serialization.serializationinfo.addvalue) methods.

```csharp
// Note lack of any special attribute.
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name", this.Name);
}
```

If such customization has been applied, the information needs to be provided during deserialization as well. That's possible by using the **serialization constructor**, where all values are read from `SerializationInfo` with one of the `Get` methods it provides.

```csharp
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
private PropertySample(SerializationInfo info, StreamingContext context)
{
this.Name = info.GetString("Name");
}
```

> [!NOTE]
> The `nameof` operator was purposely not used here, as the payload can be persisted and the property can get renamed at a later time. So even if it gets renamed (say to `FirstName` because you decide to also introduce a `LastName` property), to remain backward compatible, the serialization should still use the old name that could have been persisted somewhere.

## Serialization binder

It's recommended to use [SerializationBinder](/dotnet/api/system.runtime.serialization.serializationbinder) to control class loading and mandate what class to load. That minimizes security vulnerabilities (so only allowed types get loaded, even if the attacker modifies the payload to deserialize and load something else).

Using this type requires inheriting from it and overriding the [Type BindToType(string assemblyName, string typeName)](/dotnet/api/system.runtime.serialization.serializationbinder.bindtotype#system-runtime-serialization-serializationbinder-bindtotype(system-string-system-string)) method.

If your code uses a custom type that derives from `SerializationBinder`, it's likely that the list of serializable types is a **closed set**. If not, finding the list of all types that can get serialized and deserialized requires studying all the usages of `BinaryFormatter` in source code. **Knowing that list is crucial to determining how to move forward with the migration.**
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: "BinaryFormatter Migration Guide: Migration to DataContractSerializer"
description: "Describe the capabilities and limitations of DataContractSerializer."
ms.date: 5/31/2024
no-loc: [BinaryFormatter, Serialization]
dev_langs:
- CSharp
helpviewer_keywords:
- "BinaryFormatter"
- "serialization [WCF]"
- "serializing objects"
- "serialization"
- "objects, serializing"
---

# Migration to DataContractSerializer

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**.

```csharp
DataContractSerializer serializer = new(
type: input.GetType(),
knownTypes: new Type[]
{
typeof(MyType1),
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
@@ -0,0 +1,26 @@
---
title: "BinaryFormatter Migration Guide: Migration to MessagePack"
description: "Describe the capabilities and limitations of MessagePack serializer."
ms.date: 5/31/2024
no-loc: [BinaryFormatter, Serialization]
helpviewer_keywords:
- "BinaryFormatter"
- "serializing objects"
- "serialization"
- "objects, serializing"
---

# Migration to MessagePack

MessagePack provides a highly efficient binary serialization format, resulting in smaller message sizes compared to JSON and XML. It's [performant](https://github.com/MessagePack-CSharp/MessagePack-CSharp?tab=readme-ov-file#performance) and ships with built-in support for LZ4 compression and a full set of general-purpose expressive data types:

- By default, only public types are serializable. Private and internal structs and classes can be serialized only when `StandardResolverAllowPrivate.Options` is provided as an argument to `MessagePackSerializer.Serialize` and `MessagePackSerializer.Deserialize` methods.
- MessagePack requires each serializable type to be annotated with the `[MessagePackObject]` attribute. It's possible to avoid that by using the [ContractlessStandardResolver](https://github.com/MessagePack-CSharp/MessagePack-CSharp?tab=readme-ov-file#object-serialization), but it might cause issues with versioning in the future.
- Every serializable non-static field and a property needs to be annotated with the `[Key]` attribute. If you annotate the type with the `[MessagePackObject(keyAsPropertyName: true)]` attribute, then members don't require explicit annotations. In such case, to ignore certain public members, use the `[IgnoreMember]` attribute.
- To serialize private members, use [DynamicObjectResolverAllowPrivate](https://github.com/MessagePack-CSharp/MessagePack-CSharp?tab=readme-ov-file#object-serialization).
- `System.Runtime.Serialization` annotations can be used instead of MessagePack annotations: `[DataContract]` instead of`[MessagePackObject]`, `[DataMember]` instead of `[Key]`, and `[IgnoreDataMember]` instead of `[IgnoreMember]`. These annotations can be useful if you want to avoid a dependency on MessagePack in the library that defines serializable types.
- It supports readonly/immutable types and members. The serializer will try to use the public constructor with the best matched argument list. The constructor can be specified in an explicit way by using `[SerializationConstructor]` attribute.
- The serializer supports most frequently used built-in types and collections provided by the .NET base class libraries. You can find the full list in [official docs](https://github.com/MessagePack-CSharp/MessagePack-CSharp?tab=readme-ov-file#built-in-supported-types). It has [extension points](https://github.com/MessagePack-CSharp/MessagePack-CSharp?tab=readme-ov-file#extensions) that allow for customization.

> [!WARNING]
> MessagePack has APIs to allow for deserializing data without type restrictions. Per [MessagePack Security Notes](https://github.com/MessagePack-CSharp/MessagePack-CSharp?tab=readme-ov-file#security), these APIs should be avoided.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: "BinaryFormatter Migration Guide: Migration to protobuf-net"
description: "Describe the capabilities and limitations of protobuf-net serializer."
ms.date: 5/31/2024
no-loc: [BinaryFormatter, Serialization]
helpviewer_keywords:
- "BinaryFormatter"
- "serializing objects"
- "serialization"
- "objects, serializing"
---

# Migration to protobuf-net

[protobuf-net](https://github.com/protobuf-net/protobuf-net) is a contract-based serializer for .NET code that writes data in the _protocol buffers_ serialization format engineered by Google.

- 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.
- [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.
Loading
Loading