I have a DbSet containing the class "Week". One of the properties in the class is a list containing a class "Day".
public class Week
{
[Key]
public int Id { get; set; }
public List<Day> Days { get; set; }
public bool Ferie { get; set; }
public int WeekNo { get; set; }
public int Year { get; set; }
}
public class Day
{
[Key]
public int Id { get; set; }
public List<ProjectTask> Tasks { get; set; }
public DateTime Date { get; set; }
public int AvailableHours { get; set; }
public int HoursLeftToBook { get; set; }
public string? DayName { get; set; }
public int Priority { get; set; }
}
The database is set up as follows, with migrations. I created a news migrations after changes, so the database should be up do date.
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
Database.Migrate();
}
public DbSet<Week> Weeks { get; set; }
}
Currently, I'm testing out the database from the controller. First, I load the mockdata, save it to the database, and load the data from the database again, for display on the page.
public IActionResult Index()
{
PlannerViewModel plannerViewModel = new();
var mock = _plannerService.MockData();
// Save to database
_dataService.SaveWeeks(_plannerService.AssignProjects(mock));
// Get from database
plannerViewModel.Weeks = _dataService.GetWeeks();
return View(plannerViewModel);
}
The methods saving and returning the table, for reference:
public void SaveWeeks(List<Week> weeks)
{
_db.Weeks.AddRange(weeks);
_db.SaveChanges();
}
public List<Week> GetWeeks()
{
return _db.Weeks.ToList();
}
The weeks display fine on the page when loaded, and contain the correct days. Afterwards I comment out the lines getting the mock data and saving it to the Database, since the mock data is then already added. Just the line "_dataService.GetWeeks();" is kept (and of course the viewModel code), and the project is rerun. The weeks are now still fetched from the database correctly, but suddenly the list of days in each week is null. I checked the database, and nothing has changed in between the two runs. Are the relationships wired incorrectly? And if so, why would it work the first time, and not the following?
CodePudding user response:
It is probably because your method to fetch the data is not eager loading the related data with .Include
. When you call data that adds entities to a DbContext, that DbContext will be tracking those newly added entities and any related entities added along with them. When you read the top level entities (weeks) from the DbContext, even though you don't eagerly load the child entities (days), since the DbContext is already tracking the Days, it will still populate those child relationships.
The second time around you are relying on the data already in the database. The DbContext isn't tracking these entities, so when you get dbContext.Weeks.ToList()
you will just get the Weeks, without the associated days.
Whenever you expect related data when fetching an object graph, be sure to either:
A) Always eager load the related data:
var weeks = dbContext.Weeks.Include(w => w.Days).ToList();
B) Rely on projection to get the data you need where you don't need Entities:
var weekDetails = dbContext.Weeks
.Select(w => new WeekDetailViewModel
{
Id = w.Id,
WeekNo = w.WeekNo,
Days = w.Days.Select(d => new DayDetailViewModel
{
Id = d.Id,
// ...
}).ToList()
}).ToList();