简体   繁体   English

为什么EF查询数据需要这么长时间才能加载到内存中?

[英]Why is EF query data taking so long to load into memory?

I've got a piece of code that executes a pretty big query, with lots of joins, etc. For simplicity's sake, here's more or less the structure: 我有一段代码可以执行一个很大的查询,有很多联接,等等。为简单起见,这里大概有以下结构:

var rootQuery = MainQuery(); // IQueryable<MyClass>
var jq1 = JoinQuery1(); // IQueryable<anonymous type>
var jq2 = JoinQuery2();
...
var jq6 = JoinQuery6();
var bigQuery = from x in rootQuery
               join j1 in jq1 on x.ID equals j1.MainID into join1
               from j1 in join1.DefaultIfEmpty()
               join j2 in jq2 on x.ID equals j2.MainID into join2
               from j2 in join1.DefaultIfEmpty()
               ...
               join j6 in jq6 on x.ID equals j6.MainID into join6
               from j6 in join1.DefaultIfEmpty()
               select new {
                            x.ID,
                            x.Field1,
                            j1.FieldA,
                            j2.FieldB,
                            ...
                            j6.FieldF
                          };
var sw = Stopwatch.StartNew();
var loadedData = bigQuery.ToList();
sw.Stop();
Console.WriteLine("Time Elapsed = {0} ms", sw.ElapsedMilliseconds);

The stopwatch is showing over 30 seconds elapsed, returning 9 rows and about 30 columns of data (with no text fields). 秒表显示已过去30秒,返回9行和大约30列数据(无文本字段)。 So I sniffed the query using SQL Server Profiler, and indeed it is a monster. 因此,我使用SQL Server Profiler嗅探了该查询,的确是一个庞然大物。 I fiddled and prodded and optimized a few indexes, and got the query to execute in under 200 milliseconds. 我摆弄和试着优化了一些索引,并在200毫秒内执行了查询。 But when I run the code, it is still taking over 30 seconds to run .ToList() , even though SQL Profiler says the query part is taking under 200 ms! 但是,当我运行代码时,即使SQL Profiler说查询部分花费的时间不到200毫秒,运行.ToList()仍需要30秒钟以上!

What is going on here? 这里发生了什么? Why is .ToList taking so long to load such a small data set into memory? 为什么.ToList需要这么长时间才能将这么小的数据集加载到内存中?

Edit: I worked around the problem (see my answer below), but I'm not satisfied. 编辑:我解决了该问题(请参阅下面的答案 ),但我不满意。 Answer credit to anyone who can propose a better way, or at least explain why the object materialization is so expensive that it works out cheaper to run 7 separate queries and join them in local memory. 对任何可以提出更好方法的人,或者至少解释一下为什么对象实现如此昂贵以至于运行7个独立的查询并将它们加入本地内存的成本要低一些,这是功劳。

This seems very counter-intuitive, but I solved the problem by running the IQueryables separately (using Jon Skeet's neat NullOr extension): 这似乎很违反直觉,但是我通过单独运行IQueryables(使用Jon Skeet的整洁NullOr扩展名)解决了这个问题:

var rootQuery = MainQuery().ToList();
var jq1 = JoinQuery1().ToList();
var jq2 = JoinQuery2().ToList();
...
var jq6 = JoinQuery6().ToList();
var bigQuery = from x in rootQuery
           join j1 in jq1 on x.ID equals j1.MainID into join1
           from j1 in join1.DefaultIfEmpty()
           join j2 in jq2 on x.ID equals j2.MainID into join2
           from j2 in join1.DefaultIfEmpty()
           ...
           join j6 in jq6 on x.ID equals j6.MainID into join6
           from j6 in join1.DefaultIfEmpty()
           select new {
                x.ID,
                x.Field1,
                FieldA = j1.NullOr(j=>j.FieldA),
                FieldB = j2.NullOr(j=>j.FieldB),
                ...
                FieldF = j6.NullOr(j=>j.FieldF)
              };

The SQL side now takes longer in total (more round trips), but at least the Linq-To-Entities side is practically instantaneous. 现在,SQL端总共需要更长的时间(往返更多),但是至少Linq-To-Entities端实际上是瞬时的。

Answer credit to anyone who can explain this weird behavior! 对任何可以解释这种奇怪行为的人都表示感谢!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM