给定以下示例,使用 LINQ 查询相关表的最佳做法是什么?
What is the best practice for querying related tables using LINQ given the following example?
假设我正在从几个相关表中生成一个视图模型。这样查询的advantages/differences/disadvantages是什么:
var enrollment = db.enrollment
.Include(d => d.cohort.OldCourses.OldCourseSections.Select(f => f.OldCoursePages))
.Include(d => d.OldProgress)
.FirstOrDefault(b => b.UserID == currentuser && b.cohort.OldCourses.Titleabbrev == courseabbrev);
var viewModel = new OldSectionViewModel();
viewModel.OldCourseTitle = enrollment.cohort.OldCourses.OldCourseTitle;
viewModel.OldCourseSec_title = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSec_title;
viewModel.Meta = enrollment.cohort.OldCourses.Meta;
viewModel.Titleabbrev = enrollment.cohort.OldCourses.Titleabbrev;
viewModel.OldCourseSec_abbrev = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSec_abbrev;
viewModel.progress = currentprogress;
viewModel.pages = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourse_Page_Total;
viewModel.EnrollmentID = enrollment.EnrollmentID;
viewModel.OldCourseSectionID = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSectionID;
viewModel.OldCoursePage_title = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_title;
viewModel.OldCoursePage_HTML = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_HTML;
viewModel.OldCoursePage_Order = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_Order;
或者这样:
var viewModel = db.enrollment
.Where(b => b.UserID == currentuser && b.cohort.OldCourses.Titleabbrev == courseabbrev)
.Select(x => new OldSectionViewModel
{
OldCourseTitle = x.cohort.OldCourses.OldCourseTitle,
OldCourseSec_title = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSec_title,
Meta = x.cohort.OldCourses.Meta,
Titleabbrev = x.cohort.OldCourses.Titleabbrev,
OldCourseSec_abbrev = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSec_abbrev,
progress = currentprogress,
pages = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourse_Page_Total,
EnrollmentID = x.EnrollmentID,
OldCourseSectionID = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSectionID,
OldCoursePage_title = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_title,
OldCoursePage_HTML = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_HTML,
OldCoursePage_Order = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_Order,
}).Single();
假设'currentprogress'是一个在别处设置的整数。在我看来,第二个查询会稍微有效一些,因为我加载了我需要的特定字段,而第一个查询我加载了整个表。然而,我对这些事情的理解非常基础,我很想知道究竟哪一个更有效,为什么来自真正知道他们在说什么的人?
这两个查询都符合预先加载的条件吗?
简单地说,在第一种方法中,您将在单个查询中包含所有相关表。在第二种方法中,您将生成 N+1 个查询。
虽然一般来说您应该避免 N+1 查询情况,但这也很大程度上 取决于数据的情况。一次查询如此多的相关表会带来大量数据,而执行这些连接的性质意味着您将要减少 所有 相关项目。例如,如果您实际上只需要每个项目的第一个相关项目,那么通过执行一个大查询,您将返回大量您永远不会使用的数据。
执行 N+1 个查询可能意味着向您的服务器发送大量查询,但 "a lot" 的定义是可变的。如果它最终是 10-20 个相对较小的查询,并且您有一个具有大量资源的良好数据库服务器实例,那么它可能无关紧要,而且您最好只选择您实际需要的数据.
此外,您可以使用内存缓存,这样您可能只需要每小时 运行 这些查询一次或最多一次。您只需要研究任一选项的情况并确定最终最有效/最有意义的选项。
var viewModel = db.enrollment
.Where(b => b.UserID == currentuser && b.cohort.OldCourses.Titleabbrev == courseabbrev).ToList()
.Select(x =>
{
var oldCourseSection = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev);
var oldCourseSectionPage = oldCourseSection.OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order == currentprogress);
return new OldSectionViewModel()
{
OldCourseTitle = x.cohort.OldCourses.OldCourseTitle,
OldCourseSec_title = oldCourseSection.OldCourseSec_title,
Meta = x.cohort.OldCourses.Meta,
Titleabbrev = x.cohort.OldCourses.Titleabbrev,
OldCourseSec_abbrev = oldCourseSection.OldCourseSec_abbrev,
progress = currentprogress,
pages = oldCourseSection.OldCourse_Page_Total,
EnrollmentID = x.EnrollmentID,
OldCourseSectionID = oldCourseSection.OldCourseSectionID,
OldCoursePage_title = oldCourseSectionPage.OldCoursePage_title,
OldCoursePage_HTML = oldCourseSectionPage.OldCoursePage_HTML,
OldCoursePage_Order = oldCourseSectionPage.OldCoursePage_Order,
};}).Single();
重点是在 select 中加载变量,您需要使用方括号加上 return 关键字。这是一个关于如何做到这一点的小例子。它允许您在匿名或 class 对象的 return 之前 运行 编码(两者都有效)
public class Item { public int Price { get; set; } = 0; public string Name { get; set; } = ""; }
static void Main(string[] args)
{
var Collection = new List<Item>();
var itemPrices = Collection.Select(item =>
{
var x = 10;
var y = item.Price;
return new { ItemName = item.Name, ItemPrice = x * y };
}).ToList();
itemPrices.ForEach(itemData =>
{
Console.WriteLine(itemData.ItemName + " " + itemData.ItemPrice.ToString());
});
}
假设我正在从几个相关表中生成一个视图模型。这样查询的advantages/differences/disadvantages是什么:
var enrollment = db.enrollment
.Include(d => d.cohort.OldCourses.OldCourseSections.Select(f => f.OldCoursePages))
.Include(d => d.OldProgress)
.FirstOrDefault(b => b.UserID == currentuser && b.cohort.OldCourses.Titleabbrev == courseabbrev);
var viewModel = new OldSectionViewModel();
viewModel.OldCourseTitle = enrollment.cohort.OldCourses.OldCourseTitle;
viewModel.OldCourseSec_title = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSec_title;
viewModel.Meta = enrollment.cohort.OldCourses.Meta;
viewModel.Titleabbrev = enrollment.cohort.OldCourses.Titleabbrev;
viewModel.OldCourseSec_abbrev = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSec_abbrev;
viewModel.progress = currentprogress;
viewModel.pages = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourse_Page_Total;
viewModel.EnrollmentID = enrollment.EnrollmentID;
viewModel.OldCourseSectionID = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSectionID;
viewModel.OldCoursePage_title = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_title;
viewModel.OldCoursePage_HTML = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_HTML;
viewModel.OldCoursePage_Order = enrollment.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_Order;
或者这样:
var viewModel = db.enrollment
.Where(b => b.UserID == currentuser && b.cohort.OldCourses.Titleabbrev == courseabbrev)
.Select(x => new OldSectionViewModel
{
OldCourseTitle = x.cohort.OldCourses.OldCourseTitle,
OldCourseSec_title = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSec_title,
Meta = x.cohort.OldCourses.Meta,
Titleabbrev = x.cohort.OldCourses.Titleabbrev,
OldCourseSec_abbrev = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSec_abbrev,
progress = currentprogress,
pages = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourse_Page_Total,
EnrollmentID = x.EnrollmentID,
OldCourseSectionID = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCourseSectionID,
OldCoursePage_title = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_title,
OldCoursePage_HTML = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_HTML,
OldCoursePage_Order = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev).OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order ==
currentprogress).OldCoursePage_Order,
}).Single();
假设'currentprogress'是一个在别处设置的整数。在我看来,第二个查询会稍微有效一些,因为我加载了我需要的特定字段,而第一个查询我加载了整个表。然而,我对这些事情的理解非常基础,我很想知道究竟哪一个更有效,为什么来自真正知道他们在说什么的人?
这两个查询都符合预先加载的条件吗?
简单地说,在第一种方法中,您将在单个查询中包含所有相关表。在第二种方法中,您将生成 N+1 个查询。
虽然一般来说您应该避免 N+1 查询情况,但这也很大程度上 取决于数据的情况。一次查询如此多的相关表会带来大量数据,而执行这些连接的性质意味着您将要减少 所有 相关项目。例如,如果您实际上只需要每个项目的第一个相关项目,那么通过执行一个大查询,您将返回大量您永远不会使用的数据。
执行 N+1 个查询可能意味着向您的服务器发送大量查询,但 "a lot" 的定义是可变的。如果它最终是 10-20 个相对较小的查询,并且您有一个具有大量资源的良好数据库服务器实例,那么它可能无关紧要,而且您最好只选择您实际需要的数据.
此外,您可以使用内存缓存,这样您可能只需要每小时 运行 这些查询一次或最多一次。您只需要研究任一选项的情况并确定最终最有效/最有意义的选项。
var viewModel = db.enrollment
.Where(b => b.UserID == currentuser && b.cohort.OldCourses.Titleabbrev == courseabbrev).ToList()
.Select(x =>
{
var oldCourseSection = x.cohort.OldCourses.OldCourseSections.FirstOrDefault(f => f.OldCourseSec_abbrev == secabbrev);
var oldCourseSectionPage = oldCourseSection.OldCoursePages.FirstOrDefault(g => g.OldCoursePage_Order == currentprogress);
return new OldSectionViewModel()
{
OldCourseTitle = x.cohort.OldCourses.OldCourseTitle,
OldCourseSec_title = oldCourseSection.OldCourseSec_title,
Meta = x.cohort.OldCourses.Meta,
Titleabbrev = x.cohort.OldCourses.Titleabbrev,
OldCourseSec_abbrev = oldCourseSection.OldCourseSec_abbrev,
progress = currentprogress,
pages = oldCourseSection.OldCourse_Page_Total,
EnrollmentID = x.EnrollmentID,
OldCourseSectionID = oldCourseSection.OldCourseSectionID,
OldCoursePage_title = oldCourseSectionPage.OldCoursePage_title,
OldCoursePage_HTML = oldCourseSectionPage.OldCoursePage_HTML,
OldCoursePage_Order = oldCourseSectionPage.OldCoursePage_Order,
};}).Single();
重点是在 select 中加载变量,您需要使用方括号加上 return 关键字。这是一个关于如何做到这一点的小例子。它允许您在匿名或 class 对象的 return 之前 运行 编码(两者都有效)
public class Item { public int Price { get; set; } = 0; public string Name { get; set; } = ""; }
static void Main(string[] args)
{
var Collection = new List<Item>();
var itemPrices = Collection.Select(item =>
{
var x = 10;
var y = item.Price;
return new { ItemName = item.Name, ItemPrice = x * y };
}).ToList();
itemPrices.ForEach(itemData =>
{
Console.WriteLine(itemData.ItemName + " " + itemData.ItemPrice.ToString());
});
}