0

EDIT Driver class as requested:

public class Driver
{
    public int Id { get; set; }
    public ApplicationUser User { get; set; }
    public string DriverDirectoryTitle { get; set; }
    public string FullNames { get; set; }
    public string AlternatePhoneNumber { get; set; }
    public string IdNumber { get; set; }
    public DateTime DateOfBirth { get; set; }
    public DateTime RegistrationDate { get; set; }
    public Gender Gender { get; set; }
    public Ethnicity Ethnicity { get; set; }
    public bool Disability { get; set; }
    public string Address { get; set; }
    public City City { get; set; }
    public string Nationality { get; set; }
    public bool OwnVehicle { get; set; }
    public bool DriversLicense { get; set; }
    public Experience Experience { get; set; }
    public string Availability { get; set; }
    public Vehicle Vehicle { get; set; }
    public bool IsPremium { get; set; }
    public bool Deleted { get; set; }
}

QUESTION

My app is sort of like a Euro Truck Simulator thing which lists drivers for jobs.

I'm returning a DTO onto my page (this is ASPX Web Forms by the way) which maps closely to the Driver data model:

public class DriverViewModel
{
    public int Id { get; set; }
    public string UserId { get; set; }
    public string FullNames { get; set; }
    public string DriverDirectoryTitle { get; set; }
    public string RegistrationDate { get; set; }
    public string AlternatePhoneNumber { get; set; }
    public string IdNumber { get; set; }
    public DateTime DateOfBirth { get; set; }
    public Ethnicity Ethnicity { get; set; }
    public Experience Experience { get; set; }
    public City City { get; set; }
    public Gender Gender { get; set; }
    public List<DriverLicensesViewModel> LicenseCodes { get; set; }
    public Vehicle Vehicle { get; set; }
    public bool Disability { get; set; }
    public string Address { get; set; }
    public string Nationality { get; set; }
    public bool OwnVehicle { get; set; }
    public bool DriversLicense { get; set; }
    public string Availability { get; set; }
    public bool IsPremium { get; set; }
    public bool ReferenceChecked { get; set; }
    public bool PoliceClearance { get; set; }
}

Note that none of the relational properties are defined as virtual - so none of the data is being lazy loaded.

There's some work that I'm doing before I return the viewmodel on the page but in particular, the mapping of only some relational properties is resulting in the error below.

Here's the code for the mapping:

using (var db = new ApplicationDbContext())
{
    var drivers = db.Drivers
        .Include(x => x.User).Include(x => x.City)
        .Include(x => x.Ethnicity).Include(x => x.Experience)
        .Include(x => x.Gender).Include(x => x.Vehicle)
        .OrderBy(x => x.FullNames).ToList();

    var driverModels = new List<DriverViewModel>();
    foreach (var driver in drivers)
    {
        var driverModel = new DriverViewModel
        {
            Id = driver.Id,
            FullNames = driver.FullNames,
            DriverDirectoryTitle = driver.DriverDirectoryTitle,
            RegistrationDate = driver.RegistrationDate.ToString("ddd, dd MMM yyy"),
            AlternatePhoneNumber = driver.AlternatePhoneNumber,
            IdNumber = driver.IdNumber,
            DateOfBirth = driver.DateOfBirth,
            Ethnicity = driver.Ethnicity,
            Experience = driver.Experience,
            Gender = driver.Gender,
            Vehicle = driver.Vehicle,
            Disability = driver.Disability,
            Address = driver.Address,
            Nationality = driver.Nationality,
            OwnVehicle = driver.OwnVehicle,
            DriversLicense = driver.DriversLicense,
            Availability = driver.Availability,
            IsPremium = driver.IsPremium,
            LicenseCodes = new List<DriverLicensesViewModel>()
        };
    }
}

The code as presented here works fine and the data is loaded on the page perfectly, however, if I add the mapping for the City property into the mappings, the error occurs:

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

I've checked a few other questions such as the one linked below, but all seem to involve lazy loading the data which I'm clearly not doing:

https://stackoverflow.com/a/18398729/475766

So why does my City relation that I've added here cause the problem but none of the other relations do and how do I fix it?

Ortund
  • 8,095
  • 18
  • 71
  • 139
  • Can you post your `Driver` class? Your `DriverViewModel` isn't really relevant as it doesn't interact with EF. – Johnathan Barclay Feb 19 '20 at 13:46
  • 1
    And you're sure that `City` it'self contains no `virtual` properties? Can you set a breakpoint before your `foreach` and observe that items have `City` populated from the DB? – Johnathan Barclay Feb 19 '20 at 13:55
  • 1
    Never would have thought to look further but this was a good lesson :) Removing the `virtual` properties on the entities referenced in City as far as possible appears to have fixed it. Thanks so much! Please post an answer so I can give you fake internet points :) – Ortund Feb 19 '20 at 14:01

1 Answers1

0

If any entities within the tree contain virtual properties, they will be lazy-loaded by Entity Framework, not just the root entity.

It seems that City does contains virtual properties, so once they are first accessed (i.e. within your mapping) it will result in another SQL query to the database for the necessary data.

This will fail if your context is disposed, so the solution is to remove the virtual keyword, or eager load using Select:

db.Drivers
    .Include(x => x.City.Select(c => c.LazyProperty));

For completeness, with EF Core this would be:

db.Drivers
    .Include(x => x.City)
        .ThenInclude(c => c.LazyProperty);
Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35