我从Linq查询返回一个List,然后我必须用for循环填充其中的值。 问题是它太慢了。
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:
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);
但这绝对太慢了。 有没有办法让它更快?
答案 0 :(得分:7)
绝对太慢了。有没有办法让它更快?
也许。也许不吧。但这不是正确的问题。正确的问题是:
为什么这么慢?
如果您对第二个问题有答案,那么找出第一个问题的答案要容易得多!如果对第二个问题的答案是“因为数据库在东京而且我在罗马,而且数据包的移动速度不比光速快,这是我不可接受的减速的原因”,那么你的方式就是你移居日本的速度越快;没有多少修复查询会改变光速。
要弄清楚它为何如此慢,获取一个分析器。通过分析器运行代码,并使用它来确定您花费大部分时间的地方。然后看看你是否可以加速那部分。
答案 1 :(得分:0)
对于我所看到的,当您填充值以及转换为字典时,您正无缘无故地再次迭代formentries
2次。
如果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。
答案 2 :(得分:0)
这很慢,因为O(N*M)
N
为formentries.Count
而M
为entryvalues.Count
即使进行简单的测试,我的速度也会慢20倍以上我的类型只有1000个元素只有一个int id
字段,列表中有10000个元素,它比下面的代码慢1600多倍!
假设你的入门值是一个本地列表,而不是命中一个数据库(如果是这种情况,只需.ToList()
到一个新的变量),并假设你的FormEntryId是唯一的(它似乎来自{{ 1}}调用然后尝试这个:
.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()
循环中的每个x
值进行处理,可以有效地从bigO中删除for
。
如果这不完全正确,我相信你可以改变任何缺失的东西,以适应你的工作案例。但是,除此之外,您应该考虑使用变量名称M
与formentries
的案例。一个更容易阅读。
答案 3 :(得分:0)
有一些原因可能导致您使用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域中与数据进行连接。快乐的优化/分析/编码 - 让我们知道什么对你有用。