Serialization (LINQ to SQL)

This topic describes LINQ to SQL serialization capabilities. The paragraphs that follow provide information about how to add serialization during code generation at design time and the run-time serialization behavior of LINQ to SQL classes.

You can add serialization code at design time by either of the following methods:

Overview

The code generated by LINQ to SQL provides deferred loading capabilities by default. Deferred loading is very convenient on the mid-tier for transparent loading of data on demand. However, it is problematic for serialization, because the serializer triggers deferred loading whether deferred loading is intended or not. In effect, when an object is serialized, its transitive closure under all outbound defer-loaded references is serialized.

The LINQ to SQL serialization feature addresses this problem, primarily through two mechanisms:

Definitions

  • DataContract serializer: Default serializer used by the Windows Communication Framework (WCF) component of the .NET Framework 3.0 or later versions.

  • Unidirectional serialization: The serialized version of a class that contains only a one-way association property (to avoid a cycle). By convention, the property on the parent side of a primary-foreign key relationship is marked for serialization. The other side in a bidirectional association is not serialized.

    Unidirectional serialization is the only type of serialization supported by LINQ to SQL.

Code Example

The following code uses the traditional Customer and Order classes from the Northwind sample database, and shows how these classes are decorated with serialization attributes.

' The class is decorated with the DataContract attribute.
<Table(Name:="dbo.Customers"),  _
 DataContract()>  _
Partial Public Class Customer
    Implements System.ComponentModel.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged
// The class is decorated with the DataContract attribute.
[Table(Name="dbo.Customers")]
[DataContract()]
public partial class Customer : INotifyPropertyChanging, INotifyPropertyChanged
{
    ' Private fields are not decorated with any attributes,
    ' and are elided.
    Private _CustomerID As String

    ' Public properties are decorated with the DataMember
    ' attribute and the Order property specifying the
    ' serial number. See the Order class later in this topic
    ' for exceptions
    <Column(Storage:="_CustomerID", DbType:="NChar(5) NOT NULL", CanBeNull:=false, IsPrimaryKey:=true),  _
     DataMember(Order:=1)>  _
    Public Property CustomerID() As String
        Get
            Return Me._CustomerID
        End Get
        Set
            If ((Me._CustomerID = value)  _
                        = false) Then
                Me.OnCustomerIDChanging(value)
                Me.SendPropertyChanging
                Me._CustomerID = value
                Me.SendPropertyChanged("CustomerID")
                Me.OnCustomerIDChanged
            End If
        End Set
    End Property
    // Private fields are not decorated with any attributes, and are
    // elided.
    private string _CustomerID;

    // Public properties are decorated with the DataMember
    // attribute and the Order property specifying the serial
    // number. See the Order class later in this topic for
    // exceptions.
    public Customer()
    {
        this.Initialize();
    }
    
    [Column(Storage="_CustomerID", DbType="NChar(5) NOT NULL", CanBeNull=false, IsPrimaryKey=true)]
    [DataMember(Order=1)]
    public string CustomerID
    {
        get
        {
            return this._CustomerID;
        }
        set
        {
            if ((this._CustomerID != value))
            {
                this.OnCustomerIDChanging(value);
                this.SendPropertyChanging();
                this._CustomerID = value;
                this.SendPropertyChanged("CustomerID");
                this.OnCustomerIDChanged();
            }
        }
    }
   ' The following Association property is decorated with
   ' DataMember because it is the parent side of the
   ' relationship. The reverse property in the Order
   ' class does not have a DataMember attribute. This
   ' factor prevents a 'cycle.'
   <Association(Name:="FK_Orders_Customers", Storage:="_Orders", OtherKey:="CustomerID", DeleteRule:="NO ACTION"), _
 DataMember(Order:=13)> _
Public Property Orders() As EntitySet(Of [Order])
       Get
           Return Me._Orders
       End Get
       Set(ByVal value As EntitySet(Of [Order]))
           Me._Orders.Assign(Value)
       End Set
   End Property
    // The following Association property is decorated with
    // DataMember because it is the parent side of the
    // relationship. The reverse property in the Order class
    // does not have a DataMember attribute. This factor
    // prevents a 'cycle.'
    [Association(Name="FK_Orders_Customers", Storage="_Orders", OtherKey="CustomerID", DeleteRule="NO ACTION")]
    [DataMember(Order=13)]
    public EntitySet<Order> Orders
    {
        get
        {
            return this._Orders;
        }
        set
        {
            this._Orders.Assign(value);
        }
    }

For the Order class in the following example, only the reverse association property corresponding to the Customer class is shown for brevity. It does not have a DataMember attribute to avoid a cycle.

' The class for the Orders table is also decorated with the
' DataContract attribute.
<Table(Name:="dbo.Orders"),  _
 DataContract()>  _
Partial Public Class [Order]
    Implements System.ComponentModel.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged
// The class for the Orders table is also decorated with the
// DataContract attribute.
[Table(Name="dbo.Orders")]
[DataContract()]
public partial class Order : INotifyPropertyChanging, INotifyPropertyChanged
    ' Private fields for the Orders table are not decorated with
    ' any attributes, and are elided.
    Private _CustomerID As String

    ' Public properties are decorated with the DataMember
    ' attribute.
    ' The reverse Association property on the side of the
    ' foreign key does not have the DataMember attribute.
    <Association(Name:="FK_Orders_Customers", Storage:="_Customer", ThisKey:="CustomerID", IsForeignKey:=true)>  _
    Public Property Customer() As Customer
    // Private fields for the Orders table are not decorated with
    // any attributes, and are elided.
    private int _OrderID;

    // Public properties are decorated with the DataMember
    // attribute.
    // The reverse Association property on the side of the
    // foreign key does not have the DataMember attribute.
    [Association(Name = "FK_Orders_Customers", Storage = "_Customer", ThisKey = "CustomerID", IsForeignKey = true)]
    public Customer Customer

How to Serialize the Entities

You can serialize the entities in the codes shown in the previous section as follows;

Dim db As New Northwnd("...")

Dim cust = (From c In db.Customers _
           Where c.CustomerID = "ALFKI").Single

Dim dcs As New DataContractSerializer(GetType(Customer))

Dim sb As StringBuilder = New StringBuilder()
Dim writer As XmlWriter = XmlWriter.Create(sb)
dcs.WriteObject(writer, cust)
writer.Close()
Dim xml As String = sb.ToString()
Northwnd db = new Northwnd(@"c\northwnd.mdf");

Customer cust = db.Customers.Where(c => c.CustomerID ==
    "ALFKI").Single();

DataContractSerializer dcs = 
    new DataContractSerializer(typeof(Customer));
StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb);
dcs.WriteObject(writer, cust);
writer.Close();
string xml = sb.ToString();

Self-Recursive Relationships

Self-recursive relationships follow the same pattern. The association property corresponding to the foreign key does not have a DataMember attribute, whereas the parent property does.

Consider the following class that has two self-recursive relationships: Employee.Manager/Reports and Employee.Mentor/Mentees.

' No DataMember attribute
Public Manager As Employee
<DataMember(Order:=3)> _
Public Reports As EntitySet(Of Employee)

' No DataMember attribute
Public Mentor As Employee
<DataMember(Order:=5)> _
Public Mentees As EntitySet(Of Employee)
// No DataMember attribute.
public Employee Manager;
[DataMember(Order = 3)]
public EntitySet<Employee> Reports;

// No DataMember
public Employee Mentor;
[DataMember(Order = 5)]
public EntitySet<Employee> Mentees;

See Also

Tasks

How to: Make Entities Serializable (LINQ to SQL)

Reference

SqlMetal.exe (Code Generation Tool)

Other Resources

Background Information (LINQ to SQL)