Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

Binary Deserialization με παράκαμψη ελέγχου αλλαγής Version και αριθμού μελών

Îåêßíçóå áðü ôï ìÝëïò SValsamis. Τελευταία δημοσίευση από το μέλος Mitsaras στις 26-11-2007, 18:34. Υπάρχουν 2 απαντήσεις.
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  04-02-2006, 17:24 9234

    Binary Deserialization με παράκαμψη ελέγχου αλλαγής Version και αριθμού μελών

    Το 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)

     


    Σταύρος Βαλσάμης
    Προγραμματιστής
  •  26-11-2007, 13:42 37719 σε απάντηση της 9234

    Απ: Binary Deserialization με παράκαμψη ελέγχου αλλαγής Version και αριθμού μελών

    Ενα μεγάλο ΕΥΧΑΡΙΣΤΩ.....αυτό ακριβώς έψαχνα.
  •  26-11-2007, 18:34 37735 σε απάντηση της 37719

    Απ: Binary Deserialization με παράκαμψη ελέγχου αλλαγής Version και αριθμού μελών

    Στην έκδοση 2.0 του .net framework, υιοθετείται το Version Tolerant Serialization το οποίο στην πλειονότητα των περιπτώσεων δρα αυτόματα (τουλάχιστον στη δουλειά μου ίσχυε το παραπάνω, μέχρι μου δημιούργησα έναν custom formatter). Διορθώστε με αν κάνω λάθος.

    http://msdn2.microsoft.com/En-US/library/ms229752.aspx

    Μην αφήνετε τα media να σας "ταΐζουν"!
Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems