I'm trying to serialize a complex object with virtual navigation properties. For some reason, when I try to serialize this object using WebApi (returning it from a controller) or with JsonConvert.Serialize(myObject) all of my virtual properties are null. When I inspect my object, I can see that all of these are proxy objects. For some reason json.net is not playing nice with these proxy objects. How do I get virtual properties to serialize?
-
1Sounds like these objects are lazy-loaded EF entities, yes?Davin Tryon– Davin Tryon2013-04-16 20:26:29 +00:00Commented Apr 16, 2013 at 20:26
-
Exactly. Any thoughts?orourkedd– orourkedd2013-04-16 20:30:47 +00:00Commented Apr 16, 2013 at 20:30
-
1Only that IMHO, you shouldn't be sending EF entities out for serialization. It is usually a good idea to create another flat object that you can copy the data to before serializing. Otherwise, where is the serializer supposed to stop? It would end up serializing the entire model. Is this what you want?Davin Tryon– Davin Tryon2013-04-16 20:32:34 +00:00Commented Apr 16, 2013 at 20:32
1 Answer
The problem, as mentioned in the comments, is that trying to serialize Entity Framework properties which are lazy loaded doesn't work like that. If all proxy links were chained, you could end up serializing your entire database with a simple call.
So when serializing, anything which has not been explicitly loaded is given a null value. Whenever you try and load a proxy object, a database call is made (once per object).
You can get around this is a number of different ways. Imagine you have the following entity objects:
public class Person
{
public int ID
public string PersonName
public Address PersonAddress
}
public class Address
{
public int ID
public string AddressLine
}
Assuming you load your entities like so:
Person myPerson = ObjectContext.CreateObjectSet<Person>.Where(p => p.ID == 1);
You could do (not really recommended, as it would create two separate database calls):
Person myPerson = ObjectContext.CreateObjectSet<Person>.Where(p => p.ID == 1);
var x = myPerson.Address;
A better way would be to do:
Person myPerson = ObjectContext.CreateObjectSet<Person>
.Include("PersonAddress")
.Where(p => p.ID == 1);
This would avoid your related objects being lazy loaded entirely.
Enormous caveat
If you try and perform any nested eager loading (say you had an AddressCountry object, and you wanted to eager load by changing your include to be .Include("PersonAddress.AddressCountry")), there are severe performance problems that can arise, especially if you have large datasets.