1

this is really annoying

I have something like this:

class Person {
   ..properties id, name etc..
}
class Task {
   ..properties id, name etc..
   Person Moderator {get;set}
}
public class DataModel : DbContext {
        public DbSet<Task> Tasks { get; set; }
        public DbSet<Person> People { get; set; }
}

I can then create new tasks and add People objects to the task and save, and I can see the data correctly saved in the sql backend - each Task saved has the correct Person id saved with it and the Person with that id is saved back as well.

But when I try and get back a task, the person object is always null.

using (DataModel db = new DataModel()) {
    Task t = db.Tasks.SingleOrDefault(p => p.Id == 22);
    assert(t.Name.Lenght>0)
    assert(t.Moderator != null) // always null!!!!!!
   ....
}

What do I have to do to get the whole object graph bought back? Do I have to do a join in the SingleorDefault call? seems a bit wrong somehow.

Did I mention this is really annoying.

TIA,

BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
push 22
  • 1,172
  • 3
  • 15
  • 34

2 Answers2

3

Two options for you. By default the code first / dbContext model returns a proxy object that derives from your model (this is important to understand when you run into JSON serialization issues). The proxy object uses lazy loading of associations but only under certain circumstances. The Moderator property has to be declared as virtual so that the proxy can override it and do the lazy loading for you.

However lazy loading can create a problem called Select N+1. If in most cases you only need the Task and not the Moderator, this won't be a problem. However if you frequently display a list of tasks and their associated moderators, you will effectively have to run an extra round trip to the database for every task in that list in addition to the 1 for the original list(e.g. for a list of 100 tasks you would do 101 queries to display the tasks and their moderators).

To get around this, EF provides the Include operator, this forces the relation to load. Use it as such

Task t = db.Tasks.Include(t=>t.Moderator).SingleOrDefault(p => p.Id == 22);

Hope this helps.

Community
  • 1
  • 1
Michael Brown
  • 9,041
  • 1
  • 28
  • 37
1

You have lazy loading turned off for your Moderator property, so it will only be loaded if you explicitly do so using Load().

You can force EF to eagerly load your related Person entity by using the Include() method in your query like this:

Task t = db.Tasks.Include(x => x.Moderator).SingleOrDefault(p => p.Id == 22)

There is a pretty good overview in this article.

BrokenGlass
  • 158,293
  • 28
  • 286
  • 335