2012-02-29 102 views
0

我从Linq查询返回一个列表,之后我必须用for循环填充它中的值。 问题是它太慢了。查询和foreach的Linq优化

var formentries = (from f in db.bNetFormEntries 
      join s in db.bNetFormStatus on f.StatusID.Value equals s.StatusID into entryStatus 
      join s2 in db.bNetFormStatus on f.ExternalStatusID.Value equals s2.StatusID into entryStatus2 
      where f.FormID == formID 
      orderby f.FormEntryID descending 
      select new FormEntry 
      { 
       FormEntryID = f.FormEntryID, 
       FormID = f.FormID, 
       IPAddress = f.IpAddress, 
       UserAgent = f.UserAgent, 
       CreatedBy = f.CreatedBy, 
       CreatedDate = f.CreatedDate, 
       UpdatedBy = f.UpdatedBy, 
       UpdatedDate = f.UpdatedDate, 
       StatusID = f.StatusID, 
       StatusText = entryStatus.FirstOrDefault().Status, 
       ExternalStatusID = f.ExternalStatusID, 
       ExternalStatusText = entryStatus2.FirstOrDefault().Status 
      }).ToList(); 

,然后我以这种方式使用的:

for(var x=0; x<formentries.Count(); x++) 
{ 
    var values = (from e in entryvalues 
       where e.FormEntryID.Equals(formentries.ElementAt(x).FormEntryID) 
       select e).ToList<FormEntryValue>(); 
    formentries.ElementAt(x).Values = values; 
} 
return formentries.ToDictionary(entry => entry.FormEntryID, entry => entry); 

但绝对是太慢了。 有没有办法让它更快?

+2

每次调用formentries.Count和formentries.ElementAt都会再次执行基础查询。你应该缓存查询结果,并通过在Formentries上调用ToArray或ToList(在保存结果之前)对其进行操作,然后再通过Formentries执行你的逻辑 – Polity 2012-02-29 15:06:07

+1

@Polity它已经物化的查询 – 2012-02-29 15:13:30

+0

请不要在“C# “等等。这就是标签的用途。 – 2012-02-29 15:13:39

回答

0

对于我所看到的,您在没有任何理由的情况下多次遍历formentries - 当您填充值时,以及何时转换为字典。

如果entryvalues是一个数据库驱动的 - 即你从数据库中获取它们,然后把值田间种群在第一个查询。

如果不是的话,那么你不需要调用ToList()在第一个查询,执行循环,然后字典建立。

var formentries = from f in db.bNetFormEntries 
       join s in db.bNetFormStatus on f.StatusID.Value equals s.StatusID into entryStatus 
       join s2 in db.bNetFormStatus on f.ExternalStatusID.Value equals s2.StatusID into entryStatus2 
       where f.FormID == formID 
       orderby f.FormEntryID descending 
       select new FormEntry 
       { 
        FormEntryID = f.FormEntryID, 
        FormID = f.FormID, 
        IPAddress = f.IpAddress, 
        UserAgent = f.UserAgent, 
        CreatedBy = f.CreatedBy, 
        CreatedDate = f.CreatedDate, 
        UpdatedBy = f.UpdatedBy, 
        UpdatedDate = f.UpdatedDate, 
        StatusID = f.StatusID, 
        StatusText = entryStatus.FirstOrDefault().Status, 
        ExternalStatusID = f.ExternalStatusID, 
        ExternalStatusText = entryStatus2.FirstOrDefault().Status 
       }; 

var formEntryDictionary = new Dictionary<int, FormEntry>(); 

foreach (formEntry in formentries) 
{ 
    formentry.Values = GetValuesForFormEntry(formentry, entryvalues); 
    formEntryDict.Add(formEntry.FormEntryID, formEntry); 
} 

return formEntryDictionary; 

,值准备:

private IList<FormEntryValue> GetValuesForFormEntry(FormEntry formEntry, IEnumerable<FormEntryValue> entryValues) 
{ 
    return (from e in entryValues 
        where e.FormEntryID.Equals(formEntry.FormEntryID) 
        select e).ToList<FormEntryValue>(); 
} 

您可以更改私有方法只能接受,如果你想ENTRYID而不是整个formEntry。

7

这肯定太慢了。有没有办法让它更快?

也许吧。也许不会。但这不是一个正确的问题。正确的问题是:

为什么这么慢?

如果你有第二个问题的答案,找出第一个问题的答案要容易得多!如果第二个问题的答案是“因为数据库在东京,而我在罗马,并且数据包移动速度不比光速快是造成我无法接受的放缓的原因”,那么您的方式更快是你搬到日本;固定查询的数量不会改变光的速度。

要找出为什么这么慢,得到一个分析器。通过分析器运行代码并使用它来确定您花费大部分时间在哪里。然后看看你是否可以加快这一部分。

0

这是缓慢的,因为你的O(N*M)其中Nformentries.CountMentryvalues.Count即使是一个简单的测试,我越来越慢20倍以上,只有1000元的任何我喜欢的类型只有一个int id场,与列表中的10000元也比下面的代码慢了1600多倍!

假设你entryvalues的本地列表,而不是打一个数据库(只是它.ToList()到一个新的变量,如果某处的话),并假设你FormEntryId是唯一的(这似乎是从.ToDictionary通话那就试试这个而不是:

var entryvaluesDictionary = entryvalues.ToDictionary(entry => entry.FormEntryID, entry => entry); 
for(var x=0; x<formentries.Count; x++) 
{ 
    formentries[x] = entryvaluesDictionary[formentries[x].FormEntryID]; 
} 
return formentries.ToDictionary(entry => entry.FormEntryID, entry => entry); 

应该很长的路要走,以使得它至少变得更好

变化:的.Count代替.Count()只是因为它是最好不要调用扩展方法时,你不需要。使用字典查找val而不是通过for循环中的每个x值来有效地从bigO中删除M

如果这不完全正确,我相信你可以改变缺失的东西来适应你的工作案例。但是,顺便说一句,你应该真的考虑使用你的变量名称formentriesformEntries,只是这样一点点容易阅读。

0

对于您使用formentries的方式,这可能会有些缓慢。

  • 的formentries List<T>从上面有一个Count属性,但您呼叫的枚举Count()扩展方法来代替。此扩展可能会或可能不会有一个优化,可以检测到您正在使用具有可遵循的Count属性的集合类型,而不是漫步枚举来计算计数。
  • 类似地,formEntries.ElementAt(x)表达式被使用两次;如果他们没有优化ElementAt以确定他们正在处理像列表这样的集合,那么LINQ将不得不冗余地遍历列表以获得第x个项目。

上述评估可能会漏掉真正的问题,如果您的配置文件只是您真正知道的问题。然而,就可以避免以上,同时使你的代码显著更容易,如果你切换你如何迭代的formentries收集如下阅读:

foreach(var fe in formentries) 
{ 
    fe.Values = entryvalues 
     .Where(e => e.FormEntryID.Equals(fe.FormEntryID)) 
     .ToList<FormEntryValue>(); 
} 
return formentries.ToDictionary(entry => entry.FormEntryID, entry => entry); 

你可能已经使出了for(var x=...) ...ElementAt(x)的方法,因为你以为你不能修改由foreach循环变量fe引用的对象上的属性。

也就是说,另一点可能是一个问题是如果formentries有多个项目相同FormEntryID。这会导致循环内多次执行相同的工作。虽然顶级查询看起来是针对数据库的,但您仍然可以使用linq-to-object land中的数据进行连接。快乐的优化/分析/编码 - 让我们知道什么适合您。