We use BinaryFormatter in a C# game, to save user game progress, game levels, etc. We are running into the problem of backwards compatibility.
The aims:
The solution needs to be completely invisible to users and level designers, and minimally burden coders who want to change something (e.g. rename a field because they thought of a better name).
Some object graphs we serialize are rooted in one class, some in others. Forward compatibility is not needed.
Potentially breaking changes (and what happens when we serialize the old version and deserialize into the new):
I have read about:
My current solution:
.
for(int i = loadedData.version; i < CurrentVersion; i++)
{
// Update() takes an instance of OldVersions.VersionX.TheClass
// and returns an instance of OldVersions.VersionXPlus1.TheClass
loadedData.data = Update(loadedData.data, i);
}
Some problems with that:
This should be a really common problem. How do people usually solve it?
Due to security vulnerabilities in BinaryFormatter, the following methods are now obsolete and produce a compile-time warning with ID SYSLIB0011 . Additionally, in ASP.NET Core 5.0 and later apps, they will throw a NotSupportedException, unless the web app has re-enabled BinaryFormatter functionality.
Backward compatibility means that an app that was developed for a particular version of a platform will run on later versions of that platform. . NET Framework tries to maximize backward compatibility: Source code written for one version of . NET Framework should compile on later versions of .
BinaryFormatter uses violates 2.), which is a huge security risk because it makes possible to run any code.
This is a really old question, but it needs an up-to-date answer anyway; I know this strays slightly off topic so bear with me. Today, in 2019: I would suggest to people who happen to read this at a stage in your project where this is reasonably feasible to seriously consider using Protobuf instead of BinaryFormatter
. It has most of the advantages of a binary format (which it is) but fewer of its disadvantages.
It has a well-thought-through strategy for handling breaking changes (adding/removing fields, etc) in a way that means it's much easier for "version x" of your software to handle "version y"-generated data and the other way around. Yes, this is actually true: an older version of your app will be able to handle data serialized with a newer version of the Protobuf .proto
interface definition. (Non-present fields will simply be ignored when deserializing.)
By comparison, when running a newer versions of the code and deserializing old data, "not-present" fields in the data will be set to their type-specific default value. In that sense, handling old data is not "fully automatic" in that sense, but still a lot simpler than when using the default binary serialization libraries included with platforms like Java and .NET.
If you prefer a non-binary format, JSON is often a suitable choice. For RPC and such scenarios, Protobuf is better though and is even officially being mentioned/endorsed by Microsoft nowadays: Introduction to gRPC on ASP.NET Core. (gRPC is a technology stack built on top of Protobuf)
Tough one. I would dump binary and use XML serialization (easier to manage, tolerant to changes that are not too extreme - like adding / removing fields). In more extreme cases it is easier to write a transform (xslt perhaps) from one version to another and keep the classes clean. If opacity and small disk footprint are a requirement you can try to compress the data before writing to disk.
We got the same problem in our application with storing user profile data (grid column arrangement, filter settings ...).
In our case the problem was the AssemblyVersion.
For this problem i create a SerializationBinder
which reads the actual assembly version of
the assemblies (all assemblies get a new version number on new deployment)
with Assembly.GetExecutingAssembly().GetName().Version
.
In the overriden method BindToType
the type info is created with the new assembly version.
The deserialization is implemented 'by hand', that means
Works with all our data and since three or four releases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With