如果我在不同时间使用相同Linq查询的不同字段,Entity Framework是否会多次查询数据库?

时间:2016-08-30 09:16:44

标签: c# performance entity-framework linq

我尝试过互联网和SOF,但无法找到有用的资源。也许我可能没有使用正确的措辞来搜索。如果我之前因为这个原因而遗漏了任何问题,请告诉我,我会将此问题记下来。

我正在处理繁忙的数据库,因此我需要向数据库发送较少的查询。

如果我从代码的不同级别访问同一Linq查询的不同列,那么实体框架是否足够智能以预见所需的列并将它们全部带入或者它是否会两次调用数据库?

例如

var query = from t1 in table_1
            join t2 in table_2 on t1.col1 equals t2.col1
            where t1.EmployeeId == EmployeeId
            group new { t1, t2 } by t1.col2 into grouped
            orderby grouped.Count() descending
            select new { Column1 = grouped.Key, Column2 = grouped.Sum(g=>g.t2.col4) };

var records = query.Take(10);

// point x
var x = records.Select(a => a.Column1).ToArray();

var y = records.Select(a => a.Column2).ToArray();

EF生成查询数据库两次以便设置x和y(首先发送查询以获取Column1,然后发送另一个获取Column2)或者它是否足够聪明,知道它需要实现两个列并将两者都带来在点x

添加以澄清问题的意图:

我明白我可以简单地在查询结束时添加一个贪婪的方法。获取(10)并完成它但我试图理解我尝试的方法(在我看来,更优雅)是否有效不是让EF做出两个问题的原因。

2 个答案:

答案 0 :(得分:4)

是的,目前您的代码将生成2个将执行到数据库的查询。原因是因为您生成了2个不同的sqls:

  1. 首先是热门查询,只记录10条记录,然后只记录Column1
  2. 其次是最高查询,仅记录10条记录,然后只记录Column2
  3. 这些是两个查询的原因是因为您在ToArray个不同的Select语句中有ToArray() - >生成不同的sql。大多数linq查询都执行不同,只有当您使用ToList() / FirstOrDefault() / ToArray之类的东西时才会执行 - 那些实际上为您提供具体数据的查询。在原始查询中,您有2个不同的var records = (from t1 in table_1 join t2 in table_2 on t1.col1 equals t2.col1 where t1.EmployeeId == EmployeeId group new { t1, t2 } by t1.col2 into grouped orderby grouped.Count() descending select new { Column1 = grouped.Key, Column2 = grouped.Sum(g=>g.t2.col4) }) .Take(10).ToList(); var x = records.Select(a => a.Column1).ToArray(); var y = records.Select(a => a.Column2).ToArray(); 数据尚未检索到 - 这意味着有2个查询(一次是第一个字段,第二个是第二个)。

    以下代码将导致对数据库的单个查询

    ToList()

    在上面的解决方案中,我在过滤掉您需要的数据(Take(10))之后添加了ToString(),然后在那时它将执行到数据库。然后你将所有数据都存储在内存中,你可以对它进行任何其他的linq操作,而不需要再次进入数据库。

    添加到您的代码var query = from t1 in table_1 join t2 in table_2 on t1.col1 equals t2.col1 where t1.EmployeeId == EmployeeId group new { t1, t2 } by t1.col2 into grouped orderby grouped.Count() descending select new { Column1 = grouped.Key, Column2 = grouped.Sum(g=>g.t2.col4) }; var generatedSql = query.ToString(); // Here you will see a query that brings all records var records = query.Take(10); generatedSql = query.ToString(); // Here you will see it taking only 10 records // point x var xQuery = records.Select(a => a.Column1); generatedSql = xQuery.ToString(); // Here you will see only 1 column in query // Still nothing has been executed to DB at this point var x = xQuery.ToArray(); // And that is what will be executed here // Now you are before second execution var yQuery = records.Select(a => a.Column2); generatedSql = yQuery.ToString(); // Here you will see only the second column in query // Finally, second execution, now with the other column var y = yQuery.ToArray(); ,以便您可以在不同的点检查生成的sql。然后你就会明白何时以及正在执行什么:

    from jnpr.junos.utils.start_shell import StartShell
    from jnpr.junos import Device
    
    dev = Device(host='xxxx', user='xxxx', password='xxxx')
    dev.open()
    
    with StartShell(dev) as ss:
        op = ss.run('vty fpc0', 'vty\)#')
        print op[1]
        op = ss.run('show version', 'vty\)#')
        print op[1]
    
    dev.close()
    

答案 1 :(得分:1)

当您在EF中的实体上运行linq语句时,如果只准备Select语句(这就是为什么类型是IQueryable)。数据是懒洋洋地加载的。当您尝试使用该查询中的值时,只使用枚举器评估结果。

因此,当您将其显式转换为集合(.toList()等)时,它会尝试获取数据以填充列表,因此会触发sql命令。

它旨在提高性能。因此,如果要使用实体的特定属性,EF不会从该表中获取所有列的值