Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

LINQ to SQL Serialization bites me again


:P
On this page:

I'm mucking around with a bit of code today that's using AJAX and returning some data from my business layer that's using LINQ to SQL Entities. I mentioned some time that one issue that I ran into and continue to bump my head against is that LINQ to SQL has big issues serializing its entities. The problem with serialization of any kind is that L2S creates cirular references with default models and these circular references are blowing up serialization.

As I mentioned in my last post this causes serialization problems using any serialization format although it's possible to work around this by setting up the relationships in a certain way so that there's no circular referencing going on.

The problem is that this is not realistic in most applications and you do in fact want this circular referencing to occur with lazy loaded entities.

So today I'm plugging away at some code that's using some AJAX remote callbacks that use my own wwMethodCallback Ajax control and it of course has the same issues as the .NET native serializers. It also chokes on the circular referencing.

In my scenario I wanted to do something as simple as this:

[CallbackMethod]
public object UpdateTotals(string Pk)
{
    int pk = 0;
    int.TryParse(Pk, out pk);
 
    Entry.Load(pk);
 
    this.UpdateTotalsOnEntry();

    return Entry.Entity;
}        

This fails because the Entry entity references a Project Entity for a 1 - 1 relation ship and the Project entity in turn points back to the in a 1 to many to the entries. In my previous post I mention some ways to get around this with Xml and WCF Serialization but it doesn't help me here with my own serializer or the MS AJAX JSON serializer which don't look at the DataContract attributes (I guess I'll fix that in mine <s>).

When I run this code the app churns for a bit and then finally fails with Stack Overflow.

Besides the solution of setting up the object hierarchy to completely avoid nested references there are a couple of other ways to handle this.

The first is kind of brute force by explicitly going in and removing setting all the relationship properties to null:

[CallbackMethod]
public object UpdateTotals(string Pk)
{
    int pk = 0;
    int.TryParse(Pk, out pk);
 
    Entry.Load(pk);
 
    this.UpdateTotalsOnEntry();
 
    Entry.Entity.Project = null;
    Entry.Entity.User = null;
    Entry.Entity.Invoice = null;
 
    return Entry.Entity;
}

This actually works and results in just the actual entity to be serialized down to the client, which in the scenario above actually is exactly what I need. I got to thinking though, it would be nice to try out and also serialize the project so I tried the following code:

// *** Load Entity into Entry.Entity
Entry.Load(pk);
 
this.UpdateTotalsOnEntry();
// *** Access on project again: Fails. Project is null
dd = Entry.Entity.Project.Entered;
 
Entry.Entity.User = null;
Entry.Entity.Invoice = null;
 
// *** Kill the circular reference
Entry.Entity.Project.Entries = null;





return Entry.Entity;
 

The intent in that code is to allow project to serialize, but cut off the circular reference. Unfortunately this didn't work correctly - for some reason when I set Entry.Entity.Project.Entries to null Entry.Entity.Project also gets set to null resulting in the Project not getting serialized either. Not sure if this a bug or by design - I suppose it could be by design that if you kill a required relationship the parent object disappears?

IAC the above is probably not a good way to do this anyway as it requires way too much inside knowledge for the front end layer.

In the end the better solution was actually creating a new anonymous type and return that instead with just the data that the client actually requires. Anonymous types are great that way: The make it extremely easy to create light weight message objects for passing back to the client. In the code above I used this code which returned only a couple of values that the client actually requires:

 
[CallbackMethod]
public object UpdateTotals(string Pk)
{
    int pk = 0;
    int.TryParse(Pk, out pk);
 
    // *** Load Entity into Entry.Entity
    Entry.Load(pk);
 
    this.UpdateTotalsOnEntry();
 
    return new { TotalHours = (decimal) Entry.Entity.TotalHours, 
                 ItemTotal = (decimal) Entry.Entity.ItemTotal };
}        

This sidesteps the whole serialization issue and also reduces the amount of data on the wire significantly. Instead of the full entity going back to the client now only the two data items go back.

 
Posted in ADO.NET  ASP.NET  LINQ  

The Voices of Reason


 

Dennis
October 02, 2007

# re: LINQ to SQL Serialization bites me again

Hi Rick,

I'm really not sure if it helps, but Scott Guthrie today made an interesting post in his Blog:

http://weblogs.asp.net/scottgu/archive/2007/10/01/tip-trick-building-a-tojson-extension-method-using-net-3-5.aspx

He uses the System.Web.Script.Serialization.JavaScriptSerializer in an extension method to serialize any object to a JSON string. This would not been too interesting yet, but there is a property called RecursionLimit that may help:

public static string ToJSON(this object obj, int recursionDepth)
{
  JavaScriptSerializer serializer = new JavaScriptSerializer();
  serializer.RecursionLimit = recursionDepth;
  return serializer.Serialize(obj);
}


As I mentioned, I didn't try it myself yet, it just seemed to fit here.

HTH, Dennis

Mark Lee
December 21, 2007

# re: LINQ to SQL Serialization bites me again

There is an very good article on CodeProject.com to address the serialization issue for Linq to Sql.
http://www.codeproject.com/KB/dotnet/linqsqlserialization.aspx

Paul Riley
December 02, 2008

# re: LINQ to SQL Serialization bites me again

Looking at this example here. I feel there is a much deeper issue at hand here.

http://www.codeproject.com/KB/dotnet/linqsqlserialization.aspx

In the specified example Product / Supplier. Why would you want to add a Products property in the Supplier table. This has really confused me. I just do not understand what Linq is trying to model with this relationship. What is wrong with a Data Method GetProductBySupplier(Supplier supplier) in the Product Class. To me Linq does not appearto be doing that DAL function of mapping DB Objects(results) to Application Objects(based on the real world).

24 hours of looking at linq and I am slowly starting to think I was right about its inability to correctly abstract the database from BLL, to use this correctly I would need to add aditional mapping layers. There may be some merit in creating the mapping classes manually but then where is the time benefit in this. It would not even be that simple to create a proof of concept using Linq and then convert to using a standard DAL the work involved in re-modeling the class would be extreme

24 Hours and
- SOAP compatibility/ serialization problems
- Inheritance problems cannot use inherited classes i.e. ModelBase class

Time to park it for another day. Dissapointing!!!

nickbw
February 08, 2010

# re: LINQ to SQL Serialization bites me again

http://msdn.microsoft.com/en-us/library/bb546184.aspx

you should just be able to set the serialization to unidirectional.

nickbw
February 08, 2010

# re: LINQ to SQL Serialization bites me again

actually that is wrong, setting it to bidirectional made no difference, removing the child property (in my case did)

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024