IQueryable<> .ToString()太慢了

时间:2012-12-05 03:03:29

标签: c# entity-framework linq-to-entities

我在这个问题的答案中找到了BatchDelete:EF Code First Delete Batch From IQueryable<T>?

该方法似乎浪费了太多时间从IQueryable构建delete子句。具体来说,使用下面的IQueryable删除20.000个元素大约需要两分钟。

context.DeleteBatch(context.SomeTable.Where(x => idList.Contains(x.Id)));

所有时间都花在这一行上:

    var sql = clause.ToString();

该行是此方法的一部分,可在上面链接的原始问题上找到,但为方便起见粘贴在此处:

private static string GetClause<T>(DbContext context, IQueryable<T> clause) where T : class
{
    const string Snippet = "FROM [dbo].[";

    var sql = clause.ToString();
    var sqlFirstPart = sql.Substring(sql.IndexOf(Snippet, System.StringComparison.OrdinalIgnoreCase));

    sqlFirstPart = sqlFirstPart.Replace("AS [Extent1]", string.Empty);
    sqlFirstPart = sqlFirstPart.Replace("[Extent1].", string.Empty);

    return sqlFirstPart;
}

我想让context.SomeTable.Where(x => idList.Contains(x.Id))进入编译查询可能会有所帮助,但是AFAIK在EF 5上使用DbContext时无法编译查询。在论文中它们应该被缓存但我没有看到第二次执行的改进迹象相同的BatchDelete。

有没有办法让它更快?我想避免手动构建SQL删除语句。

3 个答案:

答案 0 :(得分:1)

IQueryable不会被缓存,每次评估它时都会使用SQL。在其上运行ToList()或ToArray()将对其进行一次评估,然后您可以将该列表用作缓存版本。

如果你想保留你的接口,你可以使用ToList()。AsQueryable(),这将传递一个缓存版本。

相关文章。 How do I cache an IQueryable object?

答案 1 :(得分:1)

在这种情况下似乎无法缓存IQueryable,因为查询包含要检查的ID列表以及每次调用中列表的更改。

每次我必须批量删除对象时,我发现避免构建查询的两分钟延迟的唯一方法是使用ExecuteSqlCommand,如下所示:

var list = string.Join("','", ids.Select(x => x.ToString()));
var qry = string.Format("DELETE FROM SomeTable WHERE Id IN ('{0}')", list);
context.Database.ExecuteSqlCommand(qry);

我现在将此标记为答案。如果建议任何其他技术不依赖于ExecuteSqlCommand,我很乐意改变答案。

答案 2 :(得分:1)

有一种EF模式可以正常工作。

它使用投影。从DB返回ONLY键。 (预测不添加到上下文中, 所以这很快。 然后用KEY仅存根POCO构建上下文,点亮保险丝......

基本上

 var deleteMagazine = Context.Set<DeadMeat>.Where(t=>t.IhateYou == true).Select(t=>t.THEKEY).toList

//Now instantiate a dummy POCO with KEY only for the list,
foreach ( var bullet in deleteMagazine)
{
context.Set<deadmeat>.attach(bullet);
context.set<deadmeat>.remove(bullet);
 // consider saving chnages every 1000 records .... performance, trial different values
if (magazineisEmpty)  // your counter logic here :-)
  context.SaveChanges
}
// shoot anyone still moving
context.SaveChanges

检查SQL server profiler ....