В предыдущей статье мы рассказали о подходе, который используется для сериализации пользовательских объектов в MultiCAD.NET API. Тогда мы говорили о принципах применения данного подхода для обеспечения совместимости версий объектов и рассмотрели самую простую ситуацию, когда новая версия объекта получается из предыдущей путем добавления дополнительных полей. Сегодня мы предлагаем вашему вниманию обзор процесса обеспечения совместимости в случае более серьёзных изменений, таких как удаление, переименование полей или изменение их типов.
Рассмотрим ситуацию, когда в новой версии объекта поля переименованы и изменены их типы. В качестве примера пользовательского объекта возьмем, уже знакомый нам, объект CrossMark
:
Структура класса данного объекта выглядит следующим образом:
[CustomEntityAttribute("1C925FA1-842B-49CD-924F-4ABF9717DB62", 2, "Crossmark", "Crossmark Sample Entity")]
[Serializable]
public class CrossMark : McCustomBase
{
private Point3d pnt1;
private Point3d pnt2;
private Point3d pnt3;
private Point3d pnt4;
private double radius;
}
Допустим, что в новой версии класса, потребовалось задавать угловые точки метки не точками, а векторами, поле radius
при этом остается без изменений:
[CustomEntityAttribute("1C925FA1-842B-49CD-924F-4ABF9717DB62", 3, "Crossmark", "Crossmark Sample Entity")]
[Serializable]
public class CrossMark : McCustomBase
{
private Vector3d v1;
private Vector3d v2;
private Vector3d v3;
private Vector3d v4;
private double radius;
}
Очевидно, что в результате изменений такого рода объекты нового класса не будут обладать совместимостью с предыдущей версией.
Для того, чтобы новая версия смогла «понимать» предыдущую, необходимо реализовать механизм чтения необходимых полей и «перевод» старого формата данных в новый. Для решения этой задачи в MultiCAD.NET может быть использован стандартный интерфейс ISerializable
, который позволяет осуществлять контролируемую сериализацию объектов.
Для реализации ISerializable
необходима имплементация двух методов:
public void GetObjectData(SerializationInfo info, StreamingContext context)
— используется для сериализации объекта.public CrossMark(SerializationInfo info, StreamingContext ctx)
— конструктор, использующийся для десериализации.
Для нашего случая эти методы будут выглядеть следующим образом:
// Serialization
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("vec1", v1);
info.AddValue("vec2", v2);
info.AddValue("vec3", v3);
info.AddValue("vec4", v4);
info.AddValue("radius", radius);
}
// Deserialization
public CrossMark(SerializationInfo info, StreamingContext ctx)
{
radius = info.GetDouble("radius");
try
{
v1 = (Vector3d)info.GetValue("vec1", typeof(Vector3d));
v2 = (Vector3d)info.GetValue("vec2", typeof(Vector3d));
v3 = (Vector3d)info.GetValue("vec3", typeof(Vector3d));
v4 = (Vector3d)info.GetValue("vec4", typeof(Vector3d));
}
catch (System.Runtime.Serialization.SerializationException)
{
Point3d pnt1 = (Point3d)info.GetValue("pnt1", typeof(Point3d));
Point3d pnt2 = (Point3d)info.GetValue("pnt2", typeof(Point3d));
Point3d pnt3 = (Point3d)info.GetValue("pnt3", typeof(Point3d));
Point3d pnt4 = (Point3d)info.GetValue("pnt4", typeof(Point3d));
v1 = pnt1.GetAsVector();
v2 = pnt2.GetAsVector();
v3 = pnt3.GetAsVector();
v4 = pnt4.GetAsVector();
}
}
Объект теперь имеет два конструктора: один из них используется при вставке объекта в чертеж, второй — при чтении объекта из чертежа. Процесс десериализации разделен на две части: чтение данных из объекта текущей версии происходит в «штатном» режиме, а данные объектов предыдущей версии зачитываются и приводятся к текущему формату в секции обработки исключения SerializationException
, которое будет выброшено при попытке десериализации несуществующих полей из предыдущей версии.
Если планируется дальнейшее развитие версий, то альтернативным вариантом может быть сериализация версии объекта в новое поле, и разделение процесса десериализации в зависимости от значения этого поля:
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
...
info.AddValue("version", 1);
}
public CrossMark(SerializationInfo info, StreamingContext ctx)
{
int version = info.GetInt("version");
switch (version)
{
case 1:
...
case 2:
...
...
}
Таким образом, мы рассмотрели базовые случаи сериализации пользовательских объектов в MultiCAD.NET при различных вариантах изменения их структуры от версии к версии. Использование описанных подходов позволит вам создать гибкий механизм управления совместимостью пользовательских объектов различных версий в рамках вашего приложения.
Вообще говоря, сериализация объектов — тема обширная и востребованная, поэтому мы решили продолжить знакомить вас с реализацией данного механизма в MultiCAD.NET и в скором времени мы представим вашему вниманию еще несколько статей по этой тематике. В том числе, мы расскажем об организации обмена данными объектов между приложениями через сериализацию во внешние базы данных, а также ответим на другие, часто возникающие вопросы. И, как всегда, ждем ваших комментариев и интересных тем для обсуждения.
Автор: ISL