在下面的示例中,使用LINQ查询相关表的最佳实践是什么?

时间:2016-10-27 13:35:28

标签: c# mysql asp.net-mvc linq

假设我正在从几个相关表格生成视图模型。以这种方式查询有什么优点/差异/缺点:

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'是其他地方设置的整数。在我看来,第二个查询将稍微更有效,因为我加载我需要的特定字段,而第一个查询我加载整个表。然而,我对这些事情的理解是非常基本的,我很想知道哪些更有效,以及为什么真正知道他们在谈论什么的人呢?

这两个查询是否都符合急切加载的要求?

2 个答案:

答案 0 :(得分:1)

简单地说,在第一种方法中,您将所有相关表包含在一个查询中。在第二种方法中,您将生成N + 1个查询。

虽然一般来说你应该避免N + 1查询情况,但它也依赖很多来解决数据发生的问题。一次查询这么多相关表格会减少大量数据,而这些连接的性质意味着你将要删除所有相关项目。例如,如果您实际上只需要像每个相关项目那样,那么通过执行一个大查询,您将返回大量您永远不会使用的数据。

执行N + 1查询可能意味着向您的服务器发送大量查询,但“很多”的定义是可变的。如果它最终成为10-20个相对较小的查询,并且你有一个拥有大量资源的良好数据库服务器实例,它可能没那么重要,而你最好,那么,只选择你真正需要的数据

此外,您可以使用内存缓存,这样您可能只需要每小时或最多运行一次这些查询。你只需要研究两种方案中发生的事情,并确定哪种方案最有效/最有意义。

答案 1 :(得分:1)

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中加载变量,你需要使用括号PLUS 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());
        });
    }