Reputation: 46
I'm working on a project that will need an Save/load function. I'm looking at two possible ways:
In my current test project I implemented serialization, but when I update a class, the whole project is unreadable because it can't be processed by the deserialization... Making it very hard to use if I want files to be backwards compatible.
At this moment i use this approach:
<System.Serializable()> _
Public MustInherit Class clsCompo
Implements ISerializable
...
Public Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData
info.AddValue("properties", Me._properties, GetType(CustomPropertyCollection))
End Sub
Public Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
Me._properties = DirectCast(info.GetValue("properties", GetType(CustomPropertyCollection)), CustomPropertyCollection)
End Sub
End Class
I'm quite lost in all this because I find it hard to handle, especially when using more than 30 different classes that all need to be serializable.
What I'm really after is a point in the "good" direction to implement a Save/Load feature that can be backwards compatible and can save different classes all in one file
EDIT: Eventually, I found a project similar to what I needed: http://www.codeproject.com/Articles/30270/XML-Serialization-of-Complex-NET-Objects
It's similar to what has been posted below.
Upvotes: 2
Views: 2688
Reputation: 2867
The simplest method I've had reasonable long-term results with is to enforce (now and forever) your own contract that every class has at least a Version property, which when combined with the type gives you a unique identifier to later tell you how to deserialize to an object. You may also want to create objects from different classes depending on the version # of your stored data, so don't do the versioning within the class, do it with eg a Persister object which works on any object implementing your stateful interface:
First use an application-wide Enum for your application's fundamental object types (not each class):
Public Enum MyObjectTypesEnum
Unspecified = 0
Fish = 1
Cow = 2
End Enum
Then your interface:
Public Interface IStateful
ReadOnly Property Version As Integer
ReadOnly Property ObjectType As MyObjectTypesEnum
Sub SetState(Info As SerializationInfo)
Sub GetState(Info As SerializationInfo)
End Interface
Step 1 in persisting your objects is to store the version and type:
Public Sub SaveTheBasics(Info As SerializationInfo, TheObject as IStateful)
Info.SetInt64(TheObject.Version,"Version")
Info.SetInt64(TheObject.ObjectType,"ObjectType")
This stores all the information you need to cope with different versions in the future:
Dim TheObject as IStateful
Select Case Info.GetInt64("ObjectType")
Case MyObjectTypesEnum.Fish
Select Case Info.GetInt64("Version")
Case Is < 2
TheObject = New OldestFishClass
Case Is < 5
TheObject = New OldFishClass
Case Else
TheObject = New NewFishClass
End Select
Case MyObjectTypesEnum.Cow
...
End Select
TheObject.SetState(Info)
etc etc.
If your application's policy is to insist the data is stored in the latest version, then when loading and upgrading from a previous version's data, you can instantiate the old class, and use that in your newest class's overloaded loading method to upgrade:
Select Case Info.GetInt64("Version")
...
Case Is < 5
Dim TempOldObject As New OldFishClass
TempOldObject.SetState(Info)
TheObject = New NewFishClass
TheObject.SetState(TempOldObject)
Case Else
TheObject = New NewFishClass
TheObject.SetState(Info)
End Select
...
...and make sure your new class knows how to load from its previous version as below.
You can use a base class which implements the Interface for all your stateful objects, and also has any properties all your objects may need:
Public MustInherit Class MyBaseClass
Implements IStateful
Public MustOverride Readonly Property VersionNumber As Integer Implements IStateful.Version
Public MustOverride Readonly Property ObjectType As MyObjectTypesEnum Implements IStateful.ObjectType
Public UniqueID As GUID
...
Public Class NewFishClass
Inherits MyBaseClass
Public Overrides ReadOnly Property VersionNumber As Integer
Get
Return 5
End Get
End Property
Public Overrides ReadOnly Property ObjectType As MyObjectTypesEnum
Get
Return MyObjectTypesEnum.Fish
End Get
End Property
'Overloaded state-setting methods (could be Constructors instead):
Public Sub SetState(Info As SerializationInfo)
Me.ScaleCount = Info.GetInt64("ScaleCount")
End Sub
Public Sub SetState(OldFish As OldFishClass)
'Upgrade from V2:
Me.ScaleCount = OldFish.LeftScales + OldFish.RightScales
End Sub
Public Sub SetState(OldestFish As OldestFishClass)
'Upgrade from V1
Me.ScaleCount = OldFish.Length * OldFish.ScalesPerUnitLength)
End Sub
etc etc.
The overriding principle here is that the contract is contained in Interfaces, and as much as possible the base class does the necessary work of complying with that contract. Then to create your 30 classes you inherit each from the base object. If you've built it right you'll be forced to provide the information necessary for correct versioning and persistance (you're also building in abstraction layers, which of course at some point you'll be grateful for).
In larger projects I extend this sort of thinking so that each object's persistable properties are held in a "State" object which derives from a base "StateBase" class which itself implements an IState interface. Persistance then acts on any StateBase object. I also use a CollectionBase class (which also inherits from my base class) which handles instantiating and filling contained objects. Often your objects need to store references to each other across sessions, so everything needs a unique ID (GUID) to recreate those references - which goes in your base object.
Upvotes: 2
Reputation: 686
You need to develop your Data Access you receive xml from your stored procedures and stream into your objects through your DAL. Then take your stored procedure xml data and serializes it into the object.
Dim obj as New YourObjectType
obj = ConvertFromXml(xml.ToString(), GetType(YourObjectType) ,System.Text.Encoding.UTF8)
Public Shared Function ConvertFromXml(ByVal xml As String, ByVal objType As System.Type, ByVal encoding As System.Text.Encoding) As Object
Dim o As Object = Nothing
Dim serializer As XmlSerializer = New XmlSerializer(objType)
Using ms As MemoryStream = New MemoryStream(encoding.GetBytes(xml))
Using xr As XmlTextReader = New XmlTextReader(ms)
o = serializer.Deserialize(xr)
End Using
End Using
Return o
End Function
Upvotes: 0