I have a project using Entity Framework Code First version 6 with Lazy loading . At the model level, he has a course that has modules. The course class is declared as follows:
public class Course : BaseEntity { public String Title { get; set; } public String Description { get; set; } public int Revision { get; set; }
My module class is declared as follows:
public class Module : BaseEntity { [ForeignKey("Course")] public Int64 CourseID { get; set; } public virtual Course Course { get; set; } public String Title { get; set; } public Int32 SequenceNo { get; set; } public override string HumanDisplay { get { return Title; } } private IList<ModuleItem> _items; public virtual IList<ModuleItem> Items { get { return _items ?? (_items = new List<ModuleItem>()); } set { _items = value; } } }
The BaseEntity class, which they both inherit from, is only to reduce code duplication for common application properties. It declares a primary key for all objects as such:
[Key] [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] public Int64 Id { get; set; }
I can include more detailed information about this, if necessary, but I canโt understand how they will intervene, and there are a bunch of non-standard frameworks there.
When I load a course editing view that already exists in the database, the Course Modules property is always zero when consumed in the MVC view , even if there should be entries . For example, an editing course with id 3 has the null Modules property, although there are several modules with a course ID of 3.
However, reverse navigation of the DOES relationship works . If I go to the view to edit Module id 5, the Course property will be set as expected, and I can access the course values. This module is associated with Course ID 3.
My Entity Framework Code First, the default configuration is used, that is, the Lazy Loading function is enabled. I checked this by checking the DBContext debugger values, but also explains why the Course property automatically works with the module on the course.
What are the possible reasons for relationships that work from module to course, but not from the Course to the Modules collection?
As you can see from the commented code in the Course class, I had this with handling the zero situation earlier, ensuring that the Modules property was never zero, but this only matters when there are no modules for the course, which is not the case here, therefore I tried changing this back to the base ICollection property to rule it out as a problem.
I also debugged when the DBC context is hosted, and this is definitely after the view code is run, so there is no danger that the DBC context will be deleted before accessing the collection.
I am not interested in explicitly using Include statements. I want to use lazy loading and EF to automatically retrieve data, and I am aware of the implications this has for performance, etc.
Ideally, I do not want to describe the relationship with the Entity Framework beyond the use of the above properties. If I need to, but please explain why, since I'm sure I successfully completed this simple parent mode - Child, 1 - many, no problem.
UPDATE 1
The controller action code is as follows
public virtual ActionResult Edit(long id = 0) { if (Session.GetUser().GetCombinedPrivilegeForEntity(Entity).CanRead) { String saveSuccess = TempData["successMessage"] as String; currentModel = GetID(id); if (currentModel == null) throw new RecordNotFoundException(id, typeof(Model).Name); currentVM = Activator.CreateInstance<ViewModel>(); currentVM.Model = currentModel; currentVM.DB = DB; currentVM.ViewMode = ViewMode.Edit; currentVM.SuccessMessage = saveSuccess; SetViewModelPermissions(); //Cache.AddEntry(Session.SessionID, Entity, currentVM.Model.Id, currentVM); if (currentModel == null) { return HttpNotFound(); } if (ForcePartial || Request.IsAjaxRequest()) return PartialView(GetViewName("Edit"), currentVM); else return View(GetViewName("Edit"), MasterName, currentVM); } else { throw new PermissionException("read", Entity); } }
If I debug the next line and check its property, it automatically populates as expected.
currentModel = GetID(id);
As @Zaphod noted, it looks like the database connection is closed before the view starts rendering. I donโt understand why this is happening, since we are still on the server side and have not yet returned the markup to the browser. Is there a way to enable lazy loading in views and close the connection only when the controller is installed?
Update 2
Actual GetID Code:
protected virtual Model GetID(long id = 0) { return DbSet.Find(id); }
DbSet is respectively set in the controller constructor to point to the DbSet in the DbContext:
public CourseController() : base() { this.DbSet = DB.Courses; }
I donโt see how DbSet somehow breaks the requests between the last line of the controller action, invoking the view and the view that starts to work.
Update 3
I had to include detailed information about where the DbContext is created, which is in the inherited controller constructor:
public ApplicationCRUDController() : base() { this.DB = eLearn.Models.DbContext.CreateContext(Session); }
This is only removed once in the inherited Dispose method of the controller, which is not called until AFTER the view is present:
protected override void Dispose(bool disposing) { if (DB != null) DB.Dispose(); base.Dispose(disposing); }
I still don't understand why the reverse relationship script will work fine, since it should be lazy loading in the view for it, since everything uses the same structure.