⬆️ ⬇️

Serialization of objects in MultiCAD.NET. Management of compatibility of drawings and proxies



When creating custom objects on the traditional C ++ API (NRX in nanoCAD, ObjectARX in AutoCAD), it is necessary to explicitly describe the recording (serialization) and reading (deserialization) of each field to ensure the objects are stored and read from the drawing file. The MultiCAD.NET API uses a more familiar .NET to developers a descriptive approach based on standard .NET serialization.



The use of serialization, insensitive to the version of objects (Version Tolerance Serialization), provides developers with a more flexible mechanism for managing the compatibility of objects of different versions than the traditional C ++ API, which provides for reading previous versions, but reading files "from the future" is impossible.



In MultiCAD.NET, when describing new versions of objects, you can specify that the newly added fields are optional, and then the drawing saved in the format of the new version of the application will be read in the previous version. Of course, the traditional approach remained unchanged, leading to the creation of proxy objects (cached graphics of objects) when the drawing was loaded into the previous version of the application.

')

Under the cat, we will discuss how to achieve compatibility of the two versions of the object, as well as how to ensure the traditional level of compatibility when new versions of the application read old drawings, but not vice versa.





Let's look at how MultiCAD.NET uses both of these approaches (VTS and the proxy object mechanism) to organize the management of object compatibility. As a visual example, consider the two versions of the cross tag user object.







Creating a class for experiments



We described the basics of creating custom primitives in MultiCAD.NET in one of the previous articles , so we will not dwell on this, but go straight to the description of the “Cross Mark” user primitive:



[CustomEntity("1C925FA1-842B-49CD-924F-4ABF9717DB62", 1, "Crossmark", "Crossmark Sample Entity")] [Serializable] public class CrossMark : McCustomBase { private Point3d pnt1; private Point3d pnt2; private Point3d pnt3; private Point3d pnt4; } 


The full application code is available here . After compiling the application and launching it with the crossmark command based on the result of user input, the following primitive will be added to the drawing:



image



Save the resulting .dwg file. Now we will slightly change our class, adding additional functionality to it.



The second version of the class and ensuring interversion compatibility of objects



In the second version of the class, we will change the geometry of our primitive by adding a circle to the center of the cross mark. As an additional field, add the radius of this circle, supplying it with the [OptionalField] attribute to mark it as optional if the earlier version of the object is deserialized. We also indicate the new version number using the VersionAdded property:



 [OptionalField(VersionAdded = 2)] private double radius = -1; 


Initialize a new variable in the constructor and add a public property to access this field:



 public CrossMark() { ... radius = 10; } [Category("Circular component")] [DisplayName("Circle radius")] [Description("Radius of circular component of the cross mark")] public double Radius { get { return radius; } set { radius = value; } } 


In order to initiate the added field after deserializing an object with a correct value, the OnDeserialized method with the [OnDeserialized] attribute will be used:



 [OnDeserialized] private void OnDeserialized(StreamingContext context) { if (radius == -1) { radius = 10; } } 




Now applications that work with newer versions of the type can also accept objects of older versions. In addition, VTS also solves the problem of backward compatibility: applications that work with old versions of objects can accept new versions: the “redundant” fields are simply ignored and the deserialization is successful.



Thus, we ensured full compatibility of our class versions. It remains only to add a circle drawing to the OnDraw() method:



 public override void OnDraw(GeometryBuilder dc) { ... dc.DrawCircle(new Point3d(pnt1.X + 25, pnt1.Y, pnt1.Z), radius); } 


The full code of the second version of the application is also available in the archive . We compile the project, run nanoCAD, load the built assembly, and launch the new version of the application with the same “crossmark” command. After specifying the insertion point, we obtain the .dwg file with the updated version of our primitive:



image



We also save the resulting file.



results


And now we will be convinced of full compatibility of both versions.



Run nanoCAD, load the second version of the assembly and open the first version of the file. The file will be successfully opened and the primitive saved in the first version is waiting for us on the screen. As soon as any action is taken that leads to object redrawing (for example, moving) the object will be redrawn in the new version taking into account the serialization constructor's work: the cross mark will be drawn with a circle of a given radius. The REGENOBJ command can also be used for this .

Close the file. Download the first version of the application and open the second version of the file. The file will also successfully load and the second version object will initially be drawn, as it was saved in the file. After redrawing, the cross mark will be displayed in the form that was defined in the first version.



The traditional approach. Top down compatibility



The VTS mechanism, which ensures compatibility from the bottom up, is not always applicable, since not all the data in new versions of objects can be considered insignificant. In order to ensure compatibility from top to bottom, when creating a new version of an object, you need to use the following attribute:



 [CustomEntity(guid, majorVersion, databaseName, localName)] 


Here, the majorVersion property specifies the main version of the object, within which full compatibility will be ensured. If new objects are created with a majorVersion whose value is greater than the previous one, then an attempt to open such objects with an older version of the code will result in the return code eMakeMeProxy . In this case, the objects in the drawing will be represented as proxy objects, which excludes the possibility of changing and editing them.



Conclusion



In this article, we looked at an example of creating two versions of a user object and the process of managing compatibility between versions in the simplest case — adding additional data fields to a new version. We will discuss the compatibility of versions of user objects with more serious changes, such as deleting, renaming fields, or changing their types in one of the following articles.



Discussion of the article is also available on our forum: forum.nanocad.ru/index.php?showtopic=6515 .

Translation of the article into English: Serializing objects in MultiCAD.NET. Managing drawings compatibility and proxy objects .

Source: https://habr.com/ru/post/219997/



All Articles