我将通过本教程来帮助我更好地理解EF结构。我目前正在使用SQL。
在此示例中,它显示了教师,办公室,学生,课程,年级和作业
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
答案 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,释放它的线程,然后在等待后根据连续点分配一个新线程。
我在示例中看到的一个警告标志是使用可为空的参数。可以使用以下方法有效地调用此方法:
如果这些组合中的任何一个无效,则应拆分或完善该方法。
回到“ 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,网络和内存使用。