简体   繁体   中英

What's the proper way to get data using Entity Framework so you can navigate through objects?

I have a .Net 4.5 MVC 5 database first project that I'm playing around with. There's a data access layer (Entity Framework 6), a business logic layer and the MVC layer.

If I have an object with relationships in the data layer:

namespace DataAccess
{
    public class Course
    {
        public int CourseID { get; set; }
        public string Title { get; set; }

        public ICollection<Lecture> Lectures { get; set; }
        public ICollection<Tutor> Tutors { get; set; }
    }
    public class Lecture
    {
        public int LectureID { get; set; }
        public string Title { get; set; }
        public DateTime Date { get; set; }
        public ICollection<Student> Students { get; set; }
    }
    public class Tutor
    {
        public int TutorID { get; set; }
        public string Name { get; set; }
    }
    public class Student
    {
        public int StudentID { get; set; }
        public string Name { get; set; }
    }
}

And in my business logic layer I have a method that gets courses:

namespace BusinessLogic
{
    public static IEnumerable<Course> GetCourses()
    {
        using (var db = new MyEntities())
        {
            return db.Courses.Include("Lectures").Include("Lectures.Students").Include("Tutors").ToList();
        }
    }
}

And I get the data using my controller like this:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var courses = BusinessLogic.GetCourses();
        return View(courses);
    }
}

Why is it, when I query my data in the Razor view like this:

var numLectures = courses.Lectures.Count;
var numStudents = courses.Lectures.Students.Count;
var tutorName = courses.Tutors.LastOrDefault().Name;

I get the application error System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection .

I know the connection is disposed after the using statement has finished and that .ToList() will let me navigate the courses object, but how do I navigate the objects inside each course (ie lectures, students, tutors etc.)?

Your navigation properties need to be declared as virtual :

namespace DataAccess
{
    public class Course
    {
        public int CourseID { get; set; }
        public string Title { get; set; }

        public virtual ICollection<Lecture> Lectures { get; set; }
        public virtual ICollection<Tutor> Tutors { get; set; }
    }
    public class Lecture
    {
        public int LectureID { get; set; }
        public string Title { get; set; }
        public DateTime Date { get; set; }
        public virtual ICollection<Student> Students { get; set; }
    }
    ...
}

When these lazy loadable properties are not marked as virtual , the EF dynamic proxies cannot override them and you will never be able to navigate from one entity to a (set of) another.

Another bit of advice: use the strongly-typed .Include when eager loading:

namespace BusinessLogic
{
    public static IEnumerable<Course> GetCourses()
    {
        using (var db = new MyEntities())
        {
            return db.Courses
                .Include(x => x.Lectures.Select(y => y.Students))
                .Include(x => x.Tutors)
                .ToList();
        }
    }
}

I think the problem is because one (or more than one) property that you are calling in your View is (are) not included in your query. Make sure you are including all the navigation properties you need in the view. Try with this query:

  using (var db = new MyEntities())
  {
    return db.Courses.db.Courses.Include(c=>c.Lectures.Select(l=>l.Students)).Include(c=>c.Tutors‌​).ToList()
  }

If you need to add another relative property that you use in your View, then add another Include call for that property.

Another thing, when you need to eager load two levels (like Lectures.Students ), you don't need to add a Include call for each level, with the call that you do for the second level is enough to include both. Could be this way:

.Include("Lectures.Students") // as you did it

Or:

.Include(c=>c.Lectures.Select(l=>l.Students))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM