Бинарная сериализация, добавление нового поля в класс - это будет работать?
-
29-09-2019 - |
Вопрос
У меня есть клиент и серверное приложение, которое общается Over .net 2.0, удаленная с использованием бинарной сериализации.
Небольшое изменение было внесено в один из интерфейса объекта передачи данных и класса реализации, ну, ну, было добавлено полное поле строк.
Если я переведите новую версию серверного приложения, будут ли мои старые клиенты продолжать работать?
Я думаю, что они будут, поскольку ничего не было удалено из интерфейса и прямой реализации, но я не уверен.
Вероятно, это сводится к другому вопросу - является бинарным пустынизатором, «достаточно умным», чтобы справиться с такой ситуацией, инициализируя поля, он не может найти данные во входном двоичном потоке, чтобы он был нуль, или будет сломаться и бросить исключение ?
Решение
Вы можете добавить атрибут в новое свойство: OptionalField
. Анкет Без атрибута Deserializer не сможет преобразовать сериализованные данные обратно в обновленное определение. Атрибут состоит в том, чтобы убедиться, что десериализатор может обработать «отсутствующие» данные изящно.
Если вы хотите установить значение по умолчанию для нового свойства, в том случае, если нет соответствующих данных для его детериализации, реализуйте IDeserializationCallback
интерфейс и установите значение по умолчанию, если таковые имеются, в полученном методе.
Другие советы
Скорее всего, это сделает исключение, вы всегда можете реализовать свой собственный сериализатор, унаследовав от ISerializable
и реализовать управление версиями, используя свои собственные методы GetObjectData
... это даст вам более жесткую степень контроля над данными, которые будут сериализованы ... Вот пример
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
public class MyFooBar : ISerializable{
private float _fVersion = 1.0;
public MyFooBar(SerializationInfo info, StreamingContext context) {
this._fVersion = info.GetSingle("FooBarVersionID");
if (this._fVersion == 1.0F) bOk = this.HandleVersionOnePtZero(info, context);
if (!bOk) throw new SerializationException(string.Format("MyFooBar: Could not handle this version {0}.", this._fVersion.ToString()));
}
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand, Flags = System.Security.Permissions.SecurityPermissionFlag.SerializationFormatter)]
public void GetObjectData(SerializationInfo info, StreamingContext context) {
info.AddValue("FooBarVersionID", this._fVersion);
if (this._fVersion == 1.0F) {
// Bool's...
info.AddValue("FooBarBool", FooBarBool);
// etc... for Version 1.0
}
if (this._fVersion == 1.1F){
// etc... for Version 1.0
}
}
}
И используйте Myfoobar в этом контексте при сериализации/десериализации, как показано ниже
public bool Deserialize(string sFileName) {
bool bSuccessful = false;
//
if (!System.IO.File.Exists(sFileName)) return false;
fuBar = new MyFooBar();
//
try {
using (FileStream fStream = new FileStream(sFileName, FileMode.Open)) {
try {
BinaryFormatter bf = new BinaryFormatter();
fuBar = (MyFooBar)bf.Deserialize(fStream);
bSuccessful = true;
} catch (System.Runtime.Serialization.SerializationException sEx) {
System.Diagnostics.Debug.WriteLine(string.Format("SERIALIZATION EXCEPTION> DETAILS ARE {0}", sEx.ToString()));
bSuccessful = false;
}
}
} catch (System.IO.IOException ioEx) {
System.Diagnostics.Debug.WriteLine(string.Format("IO EXCEPTION> DETAILS ARE {0}", ioEx.ToString()));
bSuccessful = false;
}
return (bSuccessful == true);
}
Есть более аккуратный способ сделать это в 2.0+ вверх, но я предпочитаю это.