实体框架结构

时间:2018-11-08 16:14:38

标签: c# entity-framework razor

我将通过本教程来帮助我更好地理解EF结构。我目前正在使用SQL。

https://docs.microsoft.com/en-us/aspnet/core/data/ef-rp/read-related-data?view=aspnetcore-2.1&tabs=visual-studio

在此示例中,它显示了教师,办公室,学生,课程,年级和作业

    public async Task OnGetAsync(int? id, int? courseID)
    {
        Instructor = new InstructorIndexData();
        Instructor.Instructors = await _context.Instructors
              .Include(i => i.OfficeAssignment)
              .Include(i => i.CourseAssignments)
                .ThenInclude(i => i.Course)
                    .ThenInclude(i => i.Department)
                .Include(i => i.CourseAssignments)
                    .ThenInclude(i => i.Course)
                        .ThenInclude(i => i.Enrollments)
                            .ThenInclude(i => i.Student)
              .AsNoTracking()
              .OrderBy(i => i.LastName)
              .ToListAsync();

        if (id != null)
        {
            InstructorID = id.Value;
            Instructor instructor = Instructor.Instructors.Where(
                i => i.ID == id.Value).Single();
            Instructor.Courses = instructor.CourseAssignments.Select(s => s.Course);
        }

        if (courseID != null)
        {
            CourseID = courseID.Value;
            Instructor.Enrollments = Instructor.Courses.Where(
                x => x.CourseID == courseID).Single().Enrollments;
        }
    }

为了帮助我更好地理解语法,该SQL语句是否等效?

SELECT        *
FROM            Instructor INNER JOIN
                         OfficeAssignment ON Instructor.ID = OfficeAssignment.InstructorID INNER JOIN
                         Department ON Instructor.ID = Department.InstructorID INNER JOIN
                         Course ON Department.DepartmentID = Course.DepartmentID INNER JOIN
                         Enrollment ON Course.CourseID = Enrollment.CourseID INNER JOIN
                         CourseAssignment ON Course.CourseID = CourseAssignment.CourseID INNER JOIN
                         Student ON Enrollment.StudentID = Student.ID
WHERE Instructor.ID = @ID AND Course.CourseID = @CourseID ORDER BY Instructor.Lastname

1 个答案:

答案 0 :(得分:1)

这有助于将实体用作对象,而不是将其视为表。是的,它们通常直接与基础表相关,但这是达到目的的一种手段。您可以更直接地利用关系,而不仅仅是将其视为另一种编写SQL的方式。

例如:

   Instructor.Instructors = await _context.Instructors
          .Include(i => i.OfficeAssignment)
          .Include(i => i.CourseAssignments)
            .ThenInclude(i => i.Course)
                .ThenInclude(i => i.Department)
            .Include(i => i.CourseAssignments)
                .ThenInclude(i => i.Course)
                    .ThenInclude(i => i.Enrollments)
                        .ThenInclude(i => i.Student)
          .AsNoTracking()
          .OrderBy(i => i.LastName)
          .ToListAsync();

这将大致对应于带有一堆内部联接和一个OrderBy子句的SQL语句。但是,在EF领域,这被认为是不好的做法。原因是,就像带有内部联接的SQL语句一样,您实际上在所有这些表上都执行“ SELECT *”。您真的想要所有联接表的所有列中的 all 吗?

AsNoTracking()仅告诉EF对于检索到的数据,您将不会对其进行修改,因此不必费心跟踪脏状态。这是读取操作的性能调整。

ToListAsync()将查询作为等待的操作执行,这将释放调用该方法的线程。这里没有神奇的多线程执行方法,只需调用即可移交给SQL Server,释放它的线程,然后在等待后根据连续点分配一个新线程。

我在示例中看到的一个警告标志是使用可为空的参数。可以使用以下方法有效地调用此方法:

  • 没有ID或课程ID? 和
  • 没有课程ID的ID? 和
  • 没有ID的课程ID? 和
  • ID和课程ID?

如果这些组合中的任何一个无效,则应拆分或完善该方法。

回到“ SELECT *”行为,使用EF,您可以在幕后隐藏很多功能,准备将Linq map / reduce操作转换为SQL以在服务器上运行,并返回有意义的,最少的数据集

例如:

var query = _context.Instructors.AsQueryable();
if (id.HasValue)
  query = query.Where(i => i.ID == id.Value);
query = query.OrderBy(i => i.LastName);

var instructors = await query.Select(i => new InstructorIndexData 
  {
    InstructorId = i.ID,
    // ...
    Courses = i.CourseAssignments.Select(ca => new CourseData {
      CourseId = ca.Course.ID,
      CourseName = ca.Course.Name,  
      //..
    }
  }).ToListAsync()

if (courseId.HasValue)
{
  var enrollments = await query.SelectMany(i => i.Courses.SingleOrDefault(c => c.CourseID == courseID.Value).Enrollments.Select(e => new EnrollmentData 
  {
    InstructorId = i.ID,
    EnrollmentId = e.EnrollmentID,
    CourseId = e.Course.CourseID,
    //...
  }).ToListAsync();

  // From here, group the Enrollments by Instructor ID and add them to the Instructor index data.
  var groupedEnrollments = enrollments.GroupBy(e => e.InstructorId);
  foreach(instructorId in groupedEnrollments.Keys)
  {
    var instructor = instructors.Single(i => i.InstructorId == instructorId);
    instructor.Enrollments = groupedEnrollments[instructorId].ToList();
  }
}  

现在需要注意的是,我将其基于内存,并对您的结构和所需的输出进行粗略的猜测。关键点是利用IQueryable和发出Select语句以仅提取所需的确切数据,以填充要提供给视图的对象。

我在2次查询执行中执行此操作,一次查询获取讲师,然后第二次获取查询(如果根据提供的课程ID请求)。我个人将其分为两种方法,因为我希望注册是可选的。获取一位教师和所有教师之间也有区别。如果可能返回大量数据,则应考虑使用Skip()Take()建立分页,以避免昂贵的查询阻塞CPU,网络和内存使用。