Friday, November 9, 2012

Serialization of entities (Entity Framework)

I want to tell you about interesting issue I faced when tried to serialize an entity in Microsoft Entity Framework.

I'm going to use Northwind database for demonstration purposes. Suppose you've added the Northwind model to your solution. And you are interested in three entity classes: Customer, Order and Order_Detail. Here is the corresponding diagram:
 You want to serialize particular customer with all his orders and order details. Consider the following code:

using System.Data.Objects;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Serializing;

namespace EntitiesSerialization
{
    class Program
    {
        static void Main()
        {
            using (var context = new SerializingContext())
            {
                context.ContextOptions.LazyLoadingEnabled = false;
                var query = context.Customers.Include("Orders.Order_Details");
                var customer = query.Where("it.CustomerID = @Id", new ObjectParameter("Id", "ALFKI")).First();                
                var serializer = new XmlSerializer(typeof (Customers), new[] {typeof(Orders), typeof(Order_Details)});
                serializer.Serialize(new XmlTextWriter("test.xml", Encoding.UTF8), customer);                
            }
        }
    }
}

As you can see, I use XmlSerializer to serialize the customer to xml file. Just to make sure that LazyLoading doesn't affect the solution I've turned it off and included all entities explicitly. I pass Order and Order_Details types to XmlSerializer constructor as extraTypes. Unfortunatelly this solution won't work as it should. Result xml file contains only customer fields, no orders and order details are persisted:

<?xml version="1.0" encoding="utf-8"?>
<Customers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <EntityKey>
    <EntitySetName>Customers</EntitySetName>
    <EntityContainerName>SerializingContext</EntityContainerName>
    <EntityKeyValues>
      <EntityKeyMember>
        <Key>CustomerID</Key>
        <Value xsi:type="xsd:string">ALFKI</Value>
      </EntityKeyMember>
    </EntityKeyValues>
  </EntityKey>
  <CustomerID>ALFKI</CustomerID>
  <CompanyName>Alfreds Futterkiste</CompanyName>
  <ContactName>Maria Anders</ContactName>
  <ContactTitle>Sales Representative</ContactTitle>
  <Address>Obere Str. 57</Address>
  <City>Berlin</City>
  <PostalCode>12209</PostalCode>
  <Country>Germany</Country>
  <Phone>030-0074321</Phone>
  <Fax>030-0076545</Fax>
</Customers>

One way to make this work is described here. You can use DataContractSerializer to persist all the data to xml. Like this:

DataContractSerializer serializer = new DataContractSerializer(customer.GetType());
serializer.WriteObject(new XmlTextWriter("test.xml", Encoding.UTF8), customer);

Another option is to use binary formatter. In the following code I perform sequental serialization/deserialization of the customer object:

var binaryFormatter = new BinaryFormatter();
var stream = new MemoryStream();
binaryFormatter.Serialize(stream, customer);
stream.Position = 0;
var result = binaryFormatter.Deserialize(stream);

2 comments:

  1. Hey this is very helpful but I have a question, how do you use that XML back to restore records in the database? I need to use XML files as backups, any suggestions?

    Thanks in advance!

    ReplyDelete
    Replies
    1. You can deserialize data from XML to POCO objects using DataContractSerializer as described here
      http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx
      Then you can persist these objects to database using your ORM framework capabilities.

      Delete