In This Chapter
• Serialization in Windows Communication Foundation
• Serialization in the ADO.NET Entity Framework
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.
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.
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.
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.
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.
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 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.
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.
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.
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
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.
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
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.
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.
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.
The serialization process raises four events, which are summarized in Table 41.1.
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.
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.
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.
<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.
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.
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.
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.
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.
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.