Framework Code Object First Child Navigation Null Property

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; } //private IList<Module> _modules; //public virtual IList<Module> Modules //{ // get { return _modules ?? (_modules = new List<Module>()); } // set { _modules = value; } //} public virtual ICollection<Module> Modules { 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.

+6
source share
2 answers

A model object fraud was added to the DbContext DbSet in the OnActionExecuted controller structure:

 //We seem to need to add the model to the context because the context it was added on has been disposed....for some reason. DbSet.Add(vm.Model); 

Taking this Add call out solves the problem. I still do not understand why this violates relations in only one direction. Another problem is that the code was inserted to fix another problem so that it breaks again, but I donโ€™t know what it was, so I just need to work with it!

0
source

It looks like you are using a common controller, but it does not show how the entity loads. If your connection is established before the view is completed, you will not be able to use lazy loading. For example, consider the following:

 Course firstCourse; using (var context = new TestContext()) { firstCourse = context.Courses.First(); Console.WriteLine("Course {0} has {1} modules", firstCourse.Title, firstCourse.Modules.Count); } 

It works great. Reorder things though ...

 Course firstCourse; using (var context = new TestContext()) { firstCourse = context.Courses.First(); } Console.WriteLine("Course {0} has {1} modules", firstCourse.Title, firstCourse.Modules.Count); // ObjectDisposedException when Modules tries to lazy load 

To make this work in MVC, you have several options:

  • Usage Enables or explicitly lazy loading to populate the module.
  • Make sure the DbContext is not deleted until the end of the request (if you use IoC, something like RequestLifetimeScope in Autofac).
  • Use the Model / ViewModel approach, which will display your viewmodel while the connection is open.
0
source

Source: https://habr.com/ru/post/983319/


All Articles