【问题标题】:What's the proper way to get data using Entity Framework so you can navigate through objects?使用实体框架获取数据以便您可以浏览对象的正确方法是什么?
【发布时间】:2015-06-02 05:06:20
【问题描述】:

我正在玩一个 .Net 4.5 MVC 5 数据库第一个项目。有一个数据访问层(Entity Framework 6)、一个业务逻辑层和 MVC 层。

如果我有一个在数据层中有关系的对象:

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; }
    }
}

在我的业务逻辑层中,我有一个获取课程的方法:

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

我使用我的控制器获取数据,如下所示:

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

为什么会这样,当我像这样在 Razor 视图中查询我的数据时:

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

我收到应用程序错误System.ObjectDisposedException:ObjectContext 实例已被释放,不能再用于需要连接的操作

我知道在 using 语句完成后连接已被释放,并且 .ToList() 将让我导航课程对象,但是如何导航每门课程中的对象(即讲座、学生、导师等)?

【问题讨论】:

  • 你没有包括学生。
  • 另外,您的任何导航属性都不会加载,因为它们未标记为virtual
  • 我建议您将 numLectures 'numStudents` 和 tutorName 作为 viewModel 的属性包含在控制器操作中填充的值。
  • 这是使用 MVC 的正确方法吗(即使是一次性属性也可以添加属性到视图模型)?这是否意味着我应该创建一个模型,将课程、学生、讲座等分开,然后在控制器中填充讲座、学生等的模型参数,然后将模型传递给视图?
  • 我错过了什么吗?由于coursesCourse 的集合,它不能拥有Lectures 的属性。你应该使用courses.SelectMany(c =&gt; c.Lectures).Count; 之类的东西。

标签: c# asp.net-mvc-5 entity-framework-6


【解决方案1】:

您的导航属性需要声明为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; }
    }
    ...
}

当这些延迟加载属性未标记为 virtual 时,EF 动态代理无法覆盖它们,您将永远无法从一个实体导航到另一个(一组)实体。

另外一点建议:在急切加载时使用强类型.Include

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();
        }
    }
}

【讨论】:

  • 这真的很有帮助。谢谢!我在应用程序的其他部分中遇到上下文错误,在这些部分中,我根据父级或祖先属性查询对象。对所有关系使用 .Include() 和 .Select() 可以解决问题。
【解决方案2】:

我认为问题在于您在视图中调用的一个(或多个)属性未包含在您的查询中。确保在视图中包含所有需要的导航属性。试试这个查询:

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

如果您需要添加您在视图中使用的另一个相关属性,请为该属性添加另一个 Include 调用。

另外一件事,当您需要预先加载两个级别时(例如Lectures.Students),您不需要为每个级别添加Include 调用,您为第二个级别所做的调用就足以包括两者。可能是这样的:

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

或者:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-18
    • 1970-01-01
    • 2011-07-03
    相关资源
    最近更新 更多