17

I'm getting to grips with EF code first. My domain model design doesn't seem to support the auto 'populating' child of objects when I call them in code.

Model:

public class Car
{
    [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required,MaxLength(10)]
    public string Registration { get; set; }

    [MaxLength(30)]
    public string Make { get; set; }

    [MaxLength(45)]
    public string Model { get; set; }

    [Required]
    public Coordinates Coordinates { get; set; }

    [Required]
    public Client Client { get; set; }                    
}

public class Coordinates
{
    [Key, ForeignKey("Car")]
    public int Id { get; set; }

    public double Latitude { get; set; }

    public double Longitude { get; set; }

    [Required]
    public Car Car { get; set; }
}

For example, I simply call:

public List<Car> Get()
{            
    var cars = _context.Cars.ToList();
    return cars;
}

And my object contains all the Cars from the database, but doesn't include the Coordinates. The database seed created the data correctly, but I can't get EF to automatically reference Coordinates, or Client for that matter. But I suspect once we solve one, it'll resolve the other.

What am I doing wrong, have I misunderstood how to do this?

andyp
  • 6,229
  • 3
  • 38
  • 55
Damo
  • 1,898
  • 7
  • 38
  • 58

2 Answers2

40

You have a couple of choices here:

  • To eagerly load related entities by telling EF to Include() them. For example you can load Cars including their Coordinates and Clients like this:

    public List<Car> Get()
    {            
        var cars = _context.Cars
            .Include(car => car.Coordinates)
            .Include(car => car.Client)
            .ToList();
        return cars;
    }
    
  • To lazy load related entities by declaring the navigation properties virtual thus telling EF to load them upon first access. Make sure you don't have disabled lazy loading for your context like this:

    this.Configuration.LazyLoadingEnabled = false;

A short example would look like this:

public class Car
{
    // ... the other properties like in your class definition above

    public virtual Coordinates Coordinates { get; set;}
}

public void Get()
{
    var cars = _context.Cars.ToList();
    var coordinates = cars.First().Coordinates; // EF loads the Coordinates of the first car NOW!
}
  • Explicitly load related entities to the context. The context will then populate the navigation properties for you. Looks like this:

    public List<Car> Get()
    {
        // get all cars
        var cars = _context.Cars.ToList();
    
        // get all coordinates: the context will populate the Coordinates 
        // property on the cars loaded above
        var coordinates = _context.Coordinates.ToList();
        return cars;
    }
    
andyp
  • 6,229
  • 3
  • 38
  • 55
3

The reason why you don't have the Coordinates is because it's not included in the query. There are multiple ways to include it in the result:

  1. _context.Cars.Include(car => car.Coordinates).ToList(); --- it'll fetch cars with coordinates in one query
  2. If you don't need Coordinates for all the cars, you can do the following: make the Coordinates property virtual, then when you'll get cars, you can get Coordinates for only subset of them if you need and the separate calls will be made to the database for each property "get" access. You'll also see in the debugger that EF created dynamic classes for you, so that's why you had to make it virtual
    Also see this answer for more details on this.
Community
  • 1
  • 1
yar_shukan
  • 491
  • 3
  • 10