Chapter 41. Serialization

Most real-world applications need to store, exchange, and transfer data. Due to its special nature, the .NET Framework stores data into objects and can exchange data via objects. If you need to store data only for your application, you have a lot of alternatives. The problem is when you need to exchange and transfer data with other applications. In other words, you need to think of how your objects are represented and decide whether you need to convert them into a different format. This is because another application cannot understand objects in their pure state; therefore, how information is persisted needs to be standardized. Serialization enables you to save your object’s state to disk and then re-create the object according to the specified format. With serialization, you store your data and transfer data to other applications that can re-create the information. For example, say you have an application that needs to store and transfer data to another application through a network. With the .NET Framework, you serialize your data (that is, save the result of the serialization process to a stream), transfer your data to the target application, and wait for the target application to deserialize (that is, re-create the object starting from the serialized information) your data and use it. In this chapter, you learn to implement serialization in your applications, understanding what decisions you should make if you need to transfer data to non-.NET and non-Windows applications, too.

Objects Serialization

Serializing .NET objects is the easiest serialization mode. In this particular scenario, you need a file stream where you have to place data and a formatter establishing the serialization mode. When you have the formatter instance, you invoke the Serialize method. The System.Runtime.Serialization.Formatters namespace provides two sub namespaces, Binary and Soap. They expose BinaryFormatter and SoapFormatter, respectively. The first one serializes objects in a binary way. It is efficient, but you should use it only if you are sure that your objects will be deserialized by .NET applications because such binary format is not universal. If you instead want to be sure that your objects can be shared across various applications and platforms, you should use the SoapFormatter that produces an XML-based result that is useful when working with SOAP web services.

Binary Serialization

The following example shows how you can serialize a typed collection of strings into a file on disk using the BinaryFormatter class:

Dim stringSeries As New List(Of String) From
                    {"Serialization", "demo",
                     "with VB"}

Dim targetFile As New _
    FileStream("C: empSerializedData.dat",
               FileMode.Create)
Dim formatter As New BinaryFormatter

formatter.Serialize(targetFile, stringSeries)
targetFile.Close()
formatter = Nothing


Note

The previous code example requires Imports System.IO and Imports System.Runtime.Serialization.Formatters.Binary directives.


The code creates a new file named SerializedData.dat and puts the result of the binary serialization in the file. If you examine the content of the file with the Windows Notepad, you can obtain a result similar to what is shown in Figure 41.1.

Image

Figure 41.1. Examining the result of the serialization process.

You don’t need to know how your objects are serialized, but it is interesting to understand the type of information placed into the target file, such as the serialized type, assembly information, and the actual data. To deserialize a binary file, you invoke the BinaryFormatter.Deserialize method, as shown in the following code, which you write immediately after the preceding example:

Dim sourceFile As New FileStream("C: empSerializedData.dat",
                                 FileMode.Open)

formatter = New BinaryFormatter
Dim data = CType(formatter.Deserialize(sourceFile),
                 List(Of String))

sourceFile.Close()
formatter = Nothing

'Iterates the result
For Each item In data
    Console.WriteLine(item)
Next

Notice that Deserialize returns Object; therefore, the result needs to be converted into the appropriate type that you expect. If you run the preceding code, you see on your screen how the strings from the collection are correctly listed. This kind of serialization is also straightforward because it enables serializing entire object graphs. Moreover, you can use this technique against user interface controls in Windows Forms and WPF applications to persist the state of your interface objects that can be later re-created.


Handling Serialization Exceptions

Remember to perform serialization and deserialization operations within a Try..Catch block and implement code for handling the SerializationException exception that provides information on serialization/deserialization errors.


Creating Objects Deep Copies with Serialization

In Chapter 4, “Data Types and Expressions,” I illustrated how to create objects’ copies implementing the ICloneable interface and how you can clone an object with the MemberWiseClone method. Such scenarios have a big limitation: They cannot create copies of an entire object graph. Luckily, binary serialization can instead serialize entire object graphs and thus can be used to create complete deep copies of objects. The code in Listing 41.1 shows how to accomplish this by implementing a generic method.

Listing 41.1. Implementing Deep Copy with Serialization


Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO

Public Class CreateDeepCopy

    Public Shared Function Clone(Of T)(ByVal objectToClone As T) As T

        'If the source object is null, returns the current
        'object (as a default)
        If Object.ReferenceEquals(objectToClone, Nothing) Then
            Return objectToClone
        End If

        'Creates a new formatter whose behavior is for cloning purposes
        Dim formatter As New BinaryFormatter(Nothing,
                                             New StreamingContext(
                                                 StreamingContextStates.Clone))
        'Serializes to a memory stream
        Dim ms As New MemoryStream
        Using ms
            formatter.Serialize(ms, objectToClone)

            'Gets back to the first stream byte
            ms.Seek(0, SeekOrigin.Begin)
            'Deserializes the object graph to a new T object
            Return CType(formatter.Deserialize(ms), T)
        End Using
    End Function
End Class


Because you are not limited to file streams, taking advantage of a memory stream is good in such a scenario. You invoke the preceding method as follows:

Dim result As Object = CreateDeepCopy.Clone(objectToClone)

You could also implement extension methods for providing deep copy to all types.

SOAP Serialization

SOAP serialization works similarly to binary serialization. First, you need to add a reference to the System.Runtime.Serialization.Formatters.Soap.dll assembly. Then you add an Imports System.Runtime.Serialization.Formatters.Soap directive. Then you can serialize and deserialize your objects. To continue the example of the typed collection shown in the previous section, write the following code to accomplish serialization with the SOAP formatter:

'Requires an Imports System.Runtime.Serialization.Formatters.Soap directive
Dim stringToSerialize As String = "Serialization demo with VB"

Dim targetFile As New FileStream("C: empSerializedData.xml",
                                 FileMode.Create)

Dim formatter As New SoapFormatter
formatter.Serialize(targetFile, stringToSerialize)
targetFile.Close()
formatter = Nothing

There is no difference in the syntax for the SOAP formatter if compared to the binary one.


Tip on Generic Collections

The SoapFormatter class does not enable you to serialize generic collections. This is why a simpler example against a single string is provided.


You can still examine the result of the serialization process with the Windows Notepad. Figure 41.2 shows how the target file stores information in a XML fashion.

Image

Figure 41.2. Examining the result of the SOAP serialization process.

Typically, the SOAP serialization is intended to be used when working with SOAP web services. If you want to serialize objects in a pure XML mode, you can take advantage of XML serialization, which is described in the “XML Serialization” section later in this chapter.

Providing Serialization for Custom Objects

You can make your custom objects serializable so that you can apply the previously described techniques for persisting and re-creating objects’ state. To be serializable, a class (or structure) must be decorated with the Serializable attribute. This is the most basic scenario and is represented by the following implementation of the Person class:

Imports System.Runtime.Serialization

<Serializable()>
Public Class Person
    Public Property FirstName As String
    Public Property LastName As String
    Public Property Age As Integer
    Public Property Address As String
End Class

If you do not need to get control over the serialization process, this is all you need. By the way, there can be certain situations that you need to handle. For instance, you might want to disable serialization for a member that could become obsolete if too much time passes between serialization and deserialization. Continuing the Person class example, we decide to disable serialization for the Age member because between serialization and deserialization the represented person might be older than the moment when serialization occurred. To accomplish this, you apply the NonSerialized attribute. The big problem here is that this is a field-level attribute; therefore, it cannot be applied to properties. In such situations using auto-implemented properties is not possible, so you must write them the old-fashioned way. The following code shows how you can prevent the Age member from being serialized:

<NonSerialized()> Private _age As Integer
Public Property Age As Integer
    Get
        Return _age
    End Get
    Set(ByVal value As Integer)
        _age = value
    End Set
End Property

The subsequent problem is that you need a way for assigning a valid value to nonserialized members when deserialization occurs. The most common technique is implementing the IDeserializationCallBack interface that exposes an OnDeserialization method where you can place your initialization code. The following is the revisited code for the Person class according to the last edits:

Imports System.Runtime.Serialization

<Serializable()>
Public Class Person
    Implements IDeserializationCallback


    Public Property FirstName As String
    Public Property LastName As String

    <NonSerialized()> Private _age As Integer
    Public Property Age As Integer
        Get
            Return _age
        End Get
        Set(ByVal value As Integer)
            _age = value
        End Set
    End Property

    Public Sub OnDeserialization(ByVal sender As Object) Implements _
               System.Runtime.Serialization.IDeserializationCallback.
               OnDeserialization
        'Specify the new age
        Me.Age = 32
    End Sub
End Class

When the deserialization process invokes the OnDeserialization method, members that were not serialized can be correctly initialized anyway. Another consideration that you need to take care of is versioning. When you upgrade your application to a new version, you might also want to apply some changes to your classes, such as by adding new members. This is fine but can result in problems if the previous version of your application attempts to deserialize an object produced by the new version. To solve this problem, you can mark a member as OptionalField. In this way, the deserialization process is not affected by new members and both BinaryFormatter and SoapFormatter will not throw exceptions if they encounter new members during the process. Because the OptionalField attribute works at field level, this is another situation in which you cannot take advantage of auto-implemented properties. The following code shows how you can mark the Address member in the Person class as optional:

<OptionalField()> Private _address As String
Public Property Address As String

    Get
        Return _address
    End Get
    Set(ByVal value As String)
        _address = value
    End Set
End Property

The member is still involved in the serialization process, but if a previous version of the application attempts to perform deserialization, it will not throw exceptions when it encounters this new member that was not expected.

NonSerialized Events

Visual Basic 2012 offers a feature known as NonSerialized Events that was first introduced by its predecessor. You can decorate an event with the NonSerialized attribute in custom serialization. A common scenario for applying this technique is when you work on classes that implement the INotifyPropertyChanged interface because it is more important to serialize data and not an event that just notifies the user interface of changes on data. The following code shows an example about NonSerialized events inside a class that implements INotifyPropertyChanged:

<Serializable()>
Public Class Customer
    Implements INotifyPropertyChanged

    <NonSerialized()>
    Public Event PropertyChanged(
           ByVal sender As Object,
           ByVal e As System.ComponentModel.PropertyChangedEventArgs) _
           Implements System.ComponentModel.
                      INotifyPropertyChanged.PropertyChanged

    Protected Sub OnPropertyChanged(ByVal strPropertyName As String)
        If Me.PropertyChangedEvent IsNot Nothing Then
            RaiseEvent PropertyChanged(Me,
                       New PropertyChangedEventArgs(strPropertyName))
        End If
    End Sub

XML Serialization


Note

Code examples shown in this section require Imports System.IO and Imports System.Xml.Serialization directives.


One of the main goals of serialization is to provide a way for exchanging data with other applications so that such applications can re-create objects’ state. If you want to share your objects with non-.NET applications or with applications running on different platforms, a convenient way for serializing objects is provided by the XML serialization. As you know, XML is a standard international file format for data exchange. XML files are text files organized according to a hierarchical structure and thus can be manipulated in whatever platforms and applications you want. XML serialization provides two great benefits: absolute interoperability and background compatibility. If you upgrade or modify your applications, XML format remains the same. Opposite to such benefits, XML serialization has two limitations: It cannot serialize object graphs (therefore single objects) and cannot serialize private members. XML serialization is performed by using objects exposed by the System.Xml.Serialization namespace. You can use the XmlSerializer class that requires a System.IO.Stream object for outputting serialized data and the data itself. The following code shows how you can serialize a typed collection of strings using XML serialization:

Dim stringSeries As New List(Of String) From
    {"Serialization", "demo",
     "with VB"}

Dim targetFile As New FileStream("C: empSerializedData.xml",
                                 FileMode.Create)
Dim formatter As New XmlSerializer(GetType(List(Of String)))

formatter.Serialize(targetFile, stringSeries)
targetFile.Close()
formatter = Nothing

The XmlSerializer constructor requires the specification of the data type you are going to serialize, which is accomplished via the GetType operator. To serialize data, you invoke the XmlSerializer.Serialize method. As you can see, there are no big differences with other serialization techniques shown in the previous section. To check how your data was serialized, you can open the SerializedData.xml file. You can accomplish this with an XML editor or with a web browser instead of Notepad. Figure 41.3 shows the serialization result within Internet Explorer.

Image

Figure 41.3. The XML serialization result shown in Internet Explorer.

Notice how the newly obtained file has a perfect XML structure and therefore can be shared with other applications having the ability to perform XML deserialization. To deserialize your data, you invoke the XmlSerializer.Deserialize method, as shown in the following code:

Dim sourceFile As New FileStream("C: empSerializedData.xml",
                                 FileMode.Open)

formatter = New XmlSerializer(GetType(List(Of String)))
Dim data = CType(formatter.Deserialize(sourceFile),
                 List(Of String))

sourceFile.Close()
formatter = Nothing

'Iterates the result
For Each item In data
    Console.WriteLine(item)
Next

Customizing XML Serialization

Consider the following implementation of the Person class:

Public Class Person
    Public Property FirstName As String
    Public Property LastName As String
    Public Property Age As Integer
End Class

When you serialize an instance of that Person class, you would obtain an XML representation similar to the following:

<?xml version="1.0" ?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <FirstName>Alessandro</FirstName>
  <LastName>Del Sole</LastName>
  <Age>35</Age>
</Person>

The System.Xml.Serialization namespace offers attributes for controlling output of the XML serialization to affect the target file. For example, consider the following code:

Imports System.Xml.Serialization

<XmlRoot("Contact")> Public Class Person
    <XmlIgnore()> Public Property FirstName As String
    Public Property LastName As String
    <XmlAttribute()> Public Property Age As Integer
End Class

When an instance is serialized, the output looks like the following:

<?xml version="1.0" ?>
<Contact xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
         Age="35">
  <LastName>Del Sole</LastName>
</Contact>

The XmlRoot attribute changed the name of the root element from Person to Contact. The XmlIgnore attribute prevented a property from being serialized, and the XmlAttribute attribute treated the specified member as an XML attribute instead of an XML element. You can find the complete attributes list in the dedicated page of the MSDN Library at http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlattributes(v=vs.110).aspx. The reason you should get a reference on the Internet is that XML serialization is a settled concept for most developers, whereas .NET Framework 4.5 provides a more interesting way for XML serialization, known as XAML serialization, which is covered later in this chapter and that is more important to learn.

Custom Serialization

In most cases, the .NET built-in serialization engine is good enough. But if it does not meet your particular needs, you can override the serialization process with custom serialization. This means implementing the ISerializable interface that requires the implementation of the GetObjectData method. Such a method is important because it is invoked during serialization. A custom implementation of the class constructor must also be provided. You have to first reproduce at least what built-in formatters do during serialization. The code in Listing 41.2 shows how to provide custom serialization for the Person class.

Listing 41.2. Providing Custom Serialization


Imports System.Runtime.Serialization
Imports System.Security.Permissions

<Serializable()>
Public Class Person
    Implements ISerializable

    Public Overridable Property FirstName As String
    Public Overridable Property LastName As String
    Public Overridable Property Age As Integer

    <SecurityPermission(SecurityAction.Demand,
                        SerializationFormatter:=True)>
    Protected Sub GetObjectData(ByVal info As System.Runtime.Serialization.
                                              SerializationInfo,
                                ByVal context As System.Runtime.Serialization.
                                              StreamingContext) _
                              Implements System.Runtime.Serialization.ISerializable.
                                              GetObjectData

        info.AddValue("First name", Me.FirstName)
        info.AddValue("Last name", Me.LastName)
        info.AddValue("Age", Me.Age)
    End Sub

    'At deserialization time
    Protected Sub New(ByVal info As SerializationInfo,
                      ByVal context As StreamingContext)
        MyBase.New()
        Me.FirstName = info.GetString("First name")
        Me.LastName = info.GetString("Last name")
        Me.Age = info.GetInt32("Age")
    End Sub
End Class


The GetObjectData method is invoked when you pass an object to the Serialize method of a formatter and require an information argument of type SerializationInfo. This class stores all the information needed for serialization. It exposes an AddValue method that stores data and a value utilized for recognizing data. Notice that the information is retrieved by the special constructor implementation invoked at deserialization time via GetXXX methods, where XXX corresponds to .NET types such as Int32, Boolean, Short, and so on. Also, GetObjectData is decorated with the SecurityPermission attribute demanding for permissions about the serialization formatter. This is necessary because the permission is allowed only to full-trusted code, thus intranet and Internet zones are not allowed. Both GetObjectData and the constructor are Protected so that derived classes can still take advantage of them but are prevented from being public. If you are sure that your class will not be inherited, GetObjectData can also be Private.


Inheritance Tip

When you create a class that inherits from another class where ISerializable is implemented, if you add new members, you can also provide a new implementation of both GetObjectData and the constructor.


Implementing ISerializable is not the only way for controlling serialization. You can control serialization events, too.

Serialization Events

The serialization process raises four events, which are summarized in Table 41.1.

Table 41.1. Serialization Events

Image

Serialization events are handled differently than classic events are. There is an attribute for each event that you can handle as follows:

'Invoke this method before
'serialization begins
<OnSerializing()>
Private Sub FirstMethod()

End Sub

'Invoke this method after
'serialization completes
<OnSerialized()>
Private Sub SecondMethod()

End Sub

'Invoke this method before
'deserialization begins
<OnDeserializing()>
Private Sub ThirdMethod()

End Sub

'Invoke this method after
'deserialization completes
<OnDeserialized()>
Private Sub FourthMethod()

End Sub

The runtime takes care of invoking the specified method according to the moment represented by each attribute. In this way, you can provide additional actions based on serialization events.

Serialization with XAML

This book has five chapters dedicated to the Windows Presentation Foundation technology, due to its importance in modern application development. You learned what XAML is and how you use it to define applications’ user interface. XAML offers other advantages that can be taken in completely different scenarios; one of these is serialization. The System.Xaml.dll assembly implements the System.Xaml namespace. It offers the XamlServices class, whose purpose is providing members for reading and writing XAML in serialization scenarios. Because XAML is substantially XML code that adheres to specific schemas, serialization output will be under XML format. The good news is that you are not limited in using XAML serialization only in WPF applications. You need to add a reference to System.Xaml.dll. To understand how it works, create a new console project with Visual Basic and add the required reference. The goal of the code example is to understand how entire objects’ graphs can be serialized with this technique. Consider the following implementation of the Person class:

Public Class Person
    Public Property FirstName As String
    Public Property LastName As String
    Public Property Age As Integer
    Public Property Friends As List(Of Person)
End Class

Other than the usual properties, it exposes a Friends property of type List(Of Person). This enables you to create a simple object graph. Now consider the following code that creates two instances of the Person class that populates the Friends property of the main Person instance that we serialize:

Dim oneFriend As New Person With {.LastName = "White",
                                  .FirstName = "Robert", .Age = 35}
Dim anotherFriend As New Person With {.LastName = "Red",
                                      .FirstName = "Stephen", .Age = 42}

Dim p As New Person With {.LastName = "Del Sole", .FirstName = "Alessandro",
                          .Age = 35,
                          .Friends = New List(Of Person) _
                                     From {oneFriend, anotherFriend}}

Using objects and collection initializers makes this operation straightforward. To serialize an object graph, you invoke the XamlServices.Save shared method that requires an output stream and the object to be serialized. The following code snippet demonstrates this:

Imports System.IO, System.Xaml
'...
Using target As New FileStream("C:TempPerson.xaml", FileMode.Create)
    XamlServices.Save(target, p)
End Using


Serializing Generic Collections

When you serialize generic collections, especially custom ones, ensure that they implement the IList or IDictionary interfaces; otherwise, the serialization process might not work correctly.


The previously described serialization process produces the following output:

<Person Age="35"
    FirstName="Alessandro"
    LastName="Del Sole"
    xmlns="clr-namespace:XamlSerialization;assembly=XamlSerialization"
    xmlns:scg="clr-namespace:System.Collections.Generic;
                  assembly=mscorlib"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Person.Friends>
    <scg:List x:TypeArguments="Person" Capacity="4">
      <Person Friends="{x:Null}" Age="35" FirstName="Robert"
              LastName="White" />
      <Person Friends="{x:Null}" Age="42" FirstName="Stephen"
              LastName="Red" />
    </scg:List>
  </Person.Friends>
</Person>

This technique is efficient and makes output readable. As usual in XAML files, the XAML schema is pointed to via the x namespace. The scg namespace points to the System.Collections.Generic .NET namespace, required for deserializing the content as a generic collection. Additionally, the Person.Friends element defines subsequent Person elements storing information on child Person classes being part of the Friends property. Finally, the Friends property for nested Person elements is null. (We did not define child elements for the property.) Deserializing such content is also straightforward. To accomplish this, you invoke the XamlServices.Load shared method by converting its result into the appropriate type. The following code shows how deserialization works, iterating the final result for demonstrating that deserialization was correctly performed:

Using source As New FileStream("C: empperson.xaml", FileMode.Open)
    Dim result As Person = CType(XamlServices.Load(source), Person)

    'Shows:
    'White
    'Green
    For Each p In result.Friends
        Console.WriteLine(p.LastName)
    Next
    Console.ReadLine()
End Using

XAML serialization can be used in different situations, such as persisting the state of WPF controls but also serializing entire .NET objects graphs.

Serialization in Windows Communication Foundation

In some situations serialization is required for persisting state of objects from WCF services. Starting with .NET Framework 3.0, you can serialize objects exposed by WCF services using the DataContractSerializer class (which inherits from XmlObjectSerializer). The usage of such a class is not so different from other serialization classes. The only need is that you must mark your serializable classes either with the Serializable or with the DataContract attribute and, in this case, their members with the DataMember attribute. To see how this works in code, create a new WCF service project within Visual Studio 2012 (refer to Chapter 39, “Creating and Consuming WCF Services,” for a recap) and name it WcfPersonService. Rename the default IService1 interface to IPersonService; then rename the default Service1 class to PersonService. The new service exposes a special implementation of the Person class. Listing 41.3 shows the complete code for the WCF sample service.

Listing 41.3. Exposing Serializable Objects from WCF Services


<ServiceContract()>
Public Interface IPersonService

    <OperationContract()>
    Function GetPersonFullName(ByVal onePerson As Person) As String
End Interface

<DataContract()>
Public Class Person

    <DataMember()>
    Public Property FirstName As String

    <DataMember()>
    Public Property LastName As String
End Class
Public Class PersonService
    Implements IPersonService

    Public Function GetPersonFullName(ByVal onePerson As Person) As String _
                    Implements IPersonService.GetPersonFullName

        Dim fullName As New Text.StringBuilder
        fullName.Append(onePerson.FirstName)
        fullName.Append(" ")
        fullName.Append(onePerson.LastName)
        Return fullName.ToString
    End Function
End Class


Notice how you decorate the Person class and its members with the DataContract and DataMember attributes, respectively. Now create a new console project for testing the WCF service and serialization. Name the new project as TestWcfSerialization; then add a service reference to the WcfPersonService project (refer to Chapter 39 for a recap). This adds a reference to the WCF service creating a proxy class in Visual Basic. All you need to do now is to get the instance of the service client and invoke the DataContractSerializer class that requires a stream for putting serialized data to. The code in Listing 41.4 shows both serialization and deserialization processes.

Listing 41.4. Performing WCF Serialization


Imports TestWcfSerialization.PersonServiceReference
Imports System.IO
Imports System.Runtime.Serialization

Module Module1

    Sub Main()

        Dim client As New PersonServiceClient
        Dim p As New Person With {.FirstName = "Alessandro", .LastName = "Del Sole"}

        Dim target As New FileStream("C:TempWcfSerialized.xml", FileMode.Create)
        Dim serializer As New DataContractSerializer(GetType(Person))
        serializer.WriteObject(target, p)
        target.Close()
        serializer = Nothing

        Console.ReadLine()
        Dim source As New FileStream("C:TempWcfSerialized.xml", FileMode.Open)
        serializer = New DataContractSerializer(GetType(Person))

        Dim result As Person = CType(serializer.ReadObject(source), Person)

        Console.WriteLine(result.LastName)
        Console.ReadLine()
    End Sub
End Module


In this code, you invoke the WriteObject instance method for persisting data. The method requires the file stream instance and the data instance as arguments. WriteObject can also serialize an entire object graph, similarly to the binary standard serialization. Data is also serialized to XML format. To deserialize objects, you invoke the ReadObject instance method converting the result into the appropriate type. Serialization in WCF can cause special exceptions: InvalidDataContractException, which is thrown when the data contract on the service side is badly implemented, and System.ServiceModel.QuotaExceededException, which is thrown when serialization attempts to write a number of objects greater than the allowed number. Such a number is represented by the DataContractSerializer.MaxItemsInObjectsGraph property, and the default value is Integer.MaxValue. The following snippet shows how you catch the previously mentioned exceptions:

Try
    serializer.WriteObject(target, p)
Catch ex As InvalidDataContractException
    'Data contract on the service side is wrong
Catch ex As QuotaExceededException
    'Maximum number of serializable object exceeded
Finally
    target.Close()
    serializer = Nothing
End Try

If you wonder when you would need WCF serialization, there can be several answers to your question. The most common scenarios are when you have WCF services exposing LINQ to SQL models or Entity Data Models. Data exchange from and to clients is performed via WCF serialization. This requires a little bit of work in LINQ to SQL, whereas Entity Data Models (EDMs) are serialization-enabled, which is covered in the “Serialization in the ADO.NET Entity Framework” section.

JSON Serialization

The .NET languages support the JavaScript Object Notation (JSON) serialization, offered by the System.Runtime.Serialization.Json namespace. It is particularly useful when you need to serialize objects as javascript-compliant and is used in WCF and ASP.NET Ajax applications. Conceptually, JSON serialization works like the WCF serialization illustrated previously. The only difference is that you use a DataContractJSonSerializer class that works as in the following code snippet:

Dim target As New FileStream("C:TempWcfSerialized.xml", FileMode.Create)
Dim jsonSerializer As New DataContractJsonSerializer(GetType(Person))
jsonSerializer.WriteObject(target, p)

To deserialize objects, you invoke the DataContractJsonSerializer.ReadObject method and convert the result into the appropriate type.

Serialization in the ADO.NET Entity Framework

There are two possible ways to serialize objects when working with the ADO.NET Entity Framework, depending on what code generation strategy you chose. If you generated a data model with the Code First approach or if you generated an EDM with the None code generation strategy (which is the default option in Visual Studio 2012), you simply use the Serializable attribute and techniques described in the section called “Serialization with Custom Objects.” This is because with that code generation strategy, you use Plain Old CLR Objects (POCO) that are platform independent and thus are custom business objects like the ones described previously. When you instead create EDMs with the Default code generation strategy (that is, the only way possible before .NET 4.5), entities are automatically decorated with Serializable and DataContract attributes and their members as DataMember. You can easily check this by investigating the code-behind file for EDMs. This enables binary and XML serialization for entities also in WCF scenarios. To understand how this works, create a new console project and add a new EDM wrapping the Northwind database (refer to Chapter 26, “Introducing ADO.NET Entity Framework,” for a review), including only the Customers and Orders tables. You use formatters as you did in the objects serialization with no differences. The code in Listing 41.5 shows how to accomplish this. Notice that the code works the same for both code generation strategies.

Listing 41.5. Serializing Entities from an Entity Data Model


Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO

Module Module1
    Sub Main()
        Using northwind As New NorthwindEntities
            'Retrieves the first order, as an example
            Dim anOrder As Order = northwind.Orders.Include("Customer").First

            'Same as classic objects serialization
            Dim formatter As New BinaryFormatter
            Using stream As New FileStream("C: empEFSerialization.dat",
                                FileMode.Create)
                formatter.Serialize(stream, anOrder)
            End Using

            Dim newOrder As Order
            Using source As New FileStream("C: empEFSerialization.dat",
                                FileMode.Open)
                newOrder = CType(formatter.Deserialize(source), Order)
            End Using
        End Using

        Console.ReadLine()
    End Sub
End Module


If you need to retrieve data via a WCF service, you use a DataContractSerializer by using serialization in WCF scenarios as described earlier in this chapter. Listing 41.5 shows an example of binary serialization, but you can also take advantage of other techniques described in this chapter as well.

Summary

Serialization is the capability to save objects’ state to disk (or memory) and to re-create the states later. The .NET Framework offers several serialization techniques, all provided by the System.Runtime.Serialization namespace. You can perform binary serialization via the BinaryFormatter class or SOAP serialization (XML-based mode for SOAP web services) via the SoapFormatter class. In both cases you need an output stream and then invoke the Serialize method for performing serialization. Deserialize, on the other hand, is for performing deserialization. Another common technique is the XML serialization that creates XML documents starting from your objects, which is useful if you need to exchange your data with non-.NET applications or with non-Windows applications, due to the standard format of this kind of document. If you need deep control over the serialization process, you implement the ISerializable interface that requires the implementation of the GetObjectData, where you can customize the behavior of the process other than handling serialization events. The .NET Framework 4.5 also offers WCF serialization, which uses the DataContractSerializer class or the XAML serialization that is performed via the XamlServices class. Finally, you can serialize entities from an Entity Data Model using all the preceding techniques so that you can easily exchange (or save the state of) your data without changing the programming model.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.219.220.22