Το Binary Serialization είναι πάρα πολύ χρήσιμο στο να αποθηκεύουμε objects στο δίσκο ή σε binary πεδία κάποιου πίνακα.
Το πρόβλημα είναι ότι αν προσπαθήσουμε να κάνουμε Deserialization ενώ έχει αλλάξει λίγο το Structure του object (ΠΧ προστέθηκε ένα property στο νέο τύπο) παίρνουμε exception.
Μία προσσέγιση στη λύση αυτού του προβλήματος είναι να φτιάξουμε ένα Custom SurrogateSelector και να το περάσουμε στον Binary Formatter μας.
Κατεβάστε τον κώδικα από εδώ ,αποσυμπιέστε το αρχείο και ανοίξτε το Project TestApp
Πιο κάτω είναι ο κώδικας μιας κλάσης που κάνει αυτήν ακριβώς τη δουλειά
Imports
System.Reflection
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters
Imports System.Runtime.Serialization.Formatters.binary
Imports System.IO
'Αυτό το Class κάνει deserialization ενός Buffer κάποιο συγκεκριμένου τύπου
'Η μεταβλητή changed γίνεται true αν κάποιο member έχει αλλάξει
Public Class BinarySerialization
Public Function Deserialize(ByVal Buffer As Byte(), ByVal type As System.Type, ByRef Changed As Boolean) As Object
Dim obj As Object
Changed = False
'Δημιουργώ ένα MemoryStream με περιεχόμενο το buffer
Dim stream As MemoryStream = New MemoryStream(Buffer)
'και έναν BinaryFormatter
Dim bfmt As BinaryFormatter = New BinaryFormatter
Try
'πρώτα δοκιμάζω να κάνω deserialization με τον κλασσικό τρόπο
obj = bfmt.Deserialize(stream)
'Σε περίπτωση που αποτύχει
Catch ex As SerializationException
stream.Seek(0, SeekOrigin.Begin)
'δημιουργώ έναν BinaryFormatter με δικό μου Surrogate selector
bfmt = GetFormatter(type)
'----------------------
obj = bfmt.Deserialize(stream)
Changed = True
Finally
stream.Close()
End Try
Return obj
End Function
'Δημιουργείται ένας BinaryFormatter με Custom Surrogate Selector
Private Function GetFormatter(ByVal type As System.Type) As BinaryFormatter
'Δημιουργώ τον Surrogate Selector
Dim selector As New SerializationSurrogate(type.Assembly)
'και έναν BinaryFormatter με αυτό το Selector
Return New BinaryFormatter(selector, New StreamingContext(StreamingContextStates.All))
End Function
End
Class
'Αυτό το Class κάνει την επιλογή των members του τύπου που θα
'κάνει Deserialization και είναι Implementation των ISerializtionSurrogate και ISurrogateSelector
Public
Class SerializationSurrogate
Implements ISerializationSurrogate
Implements ISurrogateSelector
Private _assembly As System.Reflection.Assembly
#
Region "Constructor"
Public Sub New(ByVal [assembly] As System.Reflection.Assembly)
_assembly = [assembly]
End Sub
#
End Region
#
Region "ISerializationSurrogate Implementation"
Function SetObjectData(ByVal obj As Object, _
ByVal info As SerializationInfo, _
ByVal context As StreamingContext, _
ByVal selector As ISurrogateSelector) As Object _
Implements ISerializationSurrogate.SetObjectData
Dim MemberName As String = String.Empty
Dim ObjectType As Type
'Για κάθε member του Serialized τύπου
For Each entry As SerializationEntry In info
MemberName = entry.Name
'To Ονομα του Member
'Aν δοκιμάσει κανείς με τον debugger θα δεί ότι αν o τύπος κάποιου member είναι
'Inherited κάποιου Base Class (Type.BaseType) τότε το όνομα του member στο entry
'εμφανίζετια σαν [BaseClassName]+[MemberName]
'ΠΧ σε ενα Property τύπου CollectionBase το List του CollectionBase θα εμφανίζεται CollectionBase+List
'Ετσι λοιπόν αν το Member είναι κάποιου τύπου που έρχεται από
'κάποιο Base Class
If MemberName.IndexOf("+") <> -1 Then
'Σπάω το Ονομα του Member για να πάρω το Ονομα του BaseType και το Ονομα του Member
'που είναι ενωμένο με "+"
Dim NameParts As String() = MemberName.Split(New Char() {"+"})
'Το πρώτο συνθετικό είναι το Ονομα του τύπου του BaseClass
Dim BaseTypeName As String = NameParts(0)
'Το δεύτερο συνθετικό είναι το Ονομα του Member
MemberName = NameParts(1)
'Ψάχνω να βρώ τον τύπο του Base Class
ObjectType = obj.GetType
Do While ObjectType.Name <> BaseTypeName
ObjectType = ObjectType.BaseType
Loop
Else
ObjectType = obj.GetType
End If
'Βρίσκω τα Members του ObjectType
Dim members As MemberInfo() = ObjectType.GetMember(MemberName, MemberTypes.Field,
BindingFlags.NonPublic Or BindingFlags.Public Or BindingFlags.Instance)
If members.Length > 0 Then
'Υπάρχει τέτοιο member στο Serialized object
Dim newMember As FieldInfo = CType(members(0), FieldInfo)
'Παίρνω το Value του SerializationEntry
Dim value As Object = entry.Value
If Not value Is Nothing Then
'Αν ο τύπος του Member δεν είναι ίδιος με τον τύπο του Νέου member
'αν δηλαδή έχει αλλάξει
If Not newMember.FieldType.IsInstanceOfType(value) Then
'τον κάνω convert
value = Convert.ChangeType(value, newMember.FieldType)
End If
End If
'Bάζω την τιμή στο Οbject
newMember.SetValue(obj, value)
End If
Next
Return Nothing
End Function
'Είναι για το Serialization το οποίο το κάνουμε με τον κλασσικό τρόπο από τον default BinaryFormatter
Sub GetObjectData(ByVal entity As Object, _
ByVal info As SerializationInfo, _
ByVal context As StreamingContext) _
Implements ISerializationSurrogate.GetObjectData
Throw New NotImplementedException("Not Supported")
End Sub
#
End Region
#
Region "ISurrogateSelector Implementation"
Function GetSurrogate(ByVal type As System.Type, _
ByVal context As StreamingContext, _
ByRef selector As ISurrogateSelector) As ISerializationSurrogate _
Implements ISurrogateSelector.GetSurrogate
If type.Assembly Is _assembly Then
selector = Me
Return Me
Else
selector = Nothing
Return Nothing
End If
End Function
Function GetNextSelector() As ISurrogateSelector _
Implements ISurrogateSelector.GetNextSelector
Return Nothing
End Function
Sub ChainSelector(ByVal selector As System.Runtime.Serialization.ISurrogateSelector) _
Implements ISurrogateSelector.ChainSelector
Throw New NotImplementedException("Not supported")
End Sub
#
End Region
End
Class
Για να καλέσουμε το Deserialize Method :
Dim
serializer As BinarySerialization = New BinarySerialization
Dim changed As Boolean = False
Dim cls As <TestClass> = serializer.Deserialize(<SerializedBuffer>, GetType(<TestClass>), changed)
Σταύρος Βαλσάμης
Προγραμματιστής