如何将LINQ查询分组以避免超时超时异常
本文关键字:超时 异常 LINQ 查询 | 更新日期: 2023-09-27 18:18:04
我的LINQ查询如下
HashSet<Guid> temp1 ; // Getting this value through another method
var Ids = temp1.Except((from temp2 in prodContext.Documents
where temp1.Contains(temp2.id)
select temp2.id)).ToList();
这里,temp1
有大约40k的值。我得到超时错误有时,我怎么能分割这个查询使用while或任何其他循环,使它不会给我超时错误。我试图在连接字符串和数据库上下文中设置连接超时,但没有任何作用。
有什么建议请提
这是一种不常见的操作,很可能是由应用程序而不是数据库在内存中更有效地执行的查询。与其尝试将集合中的所有id值发送给DB,不如让它找到具有这些id的所有项目,然后将它们全部发送给你,更合理的做法是获取所有文档id的并在应用程序端过滤它们。
var documentIds = prodContext.Documents.Select(doc => doc.id);
var Ids = temp1.Except(documentIds).ToList();
现在,根据您有多少文档,即使是理论上也可能超时。如果是这样,那么需要对所有文档id的获取进行分页。您可以使用以下方法对任何查询进行分页,以避免一次获取整个结果集:
public static IEnumerable<IEnumerable<T>> Paginate<T>(
this IQueryable<T> query,
int pageSize)
{
int page = 0;
while (true)
{
var nextPage = query.Skip(page * pageSize)
.Take(pageSize)
.ToList();
if (nextPage.Any())
yield return nextPage;
else
yield break;
page++;
}
}
你可以这样写:
var documentIds = prodContext.Documents.Select(doc => doc.id)
//play around with different batch sizes to see what works best
.Paginate(someBatchSize)
.SelectMany(x => x);
temp1.ExceptWith(documentIds);
这是一种结合分页和缓存的方法。
这种方式一次只缓存一个页面大小,以防止内存过载和防止超时。我希望这能奏效。
int pageSize = 1000;
HashSet<Guid> temp1;
List<Guid> idsFromTable = new List<Guid>();
var Ids = temp1.ToList();
for(int i = 0; true; i++)
{
//Cache the table locally to prevent logic running while selecting on page size
idsFromTable.AddRange(prodContext.Documents.Skip(pageSize * i).Take(pageSize).Select(x=> x.id));
if(idsFromTable.Any())
{
//Then use the cached list instead of the datacontext
Ids = Ids.Except(idsFromTable).ToList();
idsFromTable.Clear();
}
else
break;
}
与Servy的答案类似,您可能希望尝试对查询进行分页,而不是将它们全部拉入。这种方法的效率取决于您正在使用的数据库,我在Informix上确实得到了一些好处。在这种情况下,逻辑看起来像这样
HashSet<Guid> ids... //Got from another method
List<Guid> validIds = new List<Guid>();
const Int32 BUFFERSIZE = 1000;
var validationBuffer = new List<Guid>(BUFFERSIZE);
foreach(var g in ids)
{
validationBuffer.Add(g)
if(validationBuffer.Count == BUFFERSIZE)
{
validIds.AddRange(
prodContext.Documents
.Select(t => t.id)
.Where(g => validationBuffer.Contains(g)));
validationBuffer.Clear();
}
}
//Do last query
validIds.AddRange(
prodContext.Documents
.Select(t => t.id)
.Where(g => validationBuffer.Contains(g)));
var missingIds = ids.Except(validIds);