将非异步Linq扩展方法转换为异步 [英] Convert non-async Linq Extension method to Async

查看:31
本文介绍了将非异步Linq扩展方法转换为异步的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题与此相同:针对SQL后端的LINQ可伸缩包含方法

简介:一个用户向我的asp.net异步控制器方法发布了一个long(id)列表.控制器需要从SQL数据库中为每个ID提取两列,并将其作为json数组返回.由于我使用的是EF/Linq,如上面的链接中所述,因此我使用了Contains方法,如下所示:

Synopsis: A User posts a list of longs (ids) to my asp.net async controller method. The controller needs to pull two columns from the SQL database for each id and returns it as json array. Since I'm using EF/Linq, as mentioned in the link above, I use the Contains method as such:

long[] ids;   //Assume that the posted list of ids to the controller method

var courses = await db.Courses.AsNoTracking().Where(x => ids.Contains(x.id))
.Select(x => new JRecord { id = x.id, name = x.name, status = x.status})
.ToListAsync();

return Request.CreateResponse(HttpStatus.OK, courses);

EF将Contains转换为SQL IN语句.问题是大多数情况下,ID列表只有几百个就可以了,但是用户也可以选择几千个条目,这会导致查询速度非常慢或查询完全失败.

EF converts Contains into SQL IN statement. The problem is majority of the time, list of ids is few 100 which is fine, but a user could also select few thousand entries which results in a really slow query or query failing altogether.

作者(在上面的链接中)发布了以下另一个问题的解决方案,其中Linq Extension只是将ID数组拆分为块,将每个ID数组作为单独的&较小的查询,然后将所有查询的结果合并回一个列表中.原因不是要提高性能,而是要确保在提供大量ID时查询不会失败.

The author (in link above) posted following solution to another problem where Linq Extension just splits the IDs array into chunks, runs each one as a separate & smaller query, then merges the results from all the queries back into a single list. The reason is not to improve performance but to ensure the Query doesn't fail when a lot of ids are provided.

他的代码: https://stackoverflow.com/a/6852288/934257

public static IEnumerable<IEnumerable<T>> ToChunks<T>(this IEnumerable<T> enumerable, int chunkSize)
{
     int itemsReturned = 0;
     var list = enumerable.ToList(); // Prevent multiple execution of IEnumerable.
     int count = list.Count;
     while (itemsReturned < count)
     {
          int currentChunkSize = Math.Min(chunkSize, count - itemsReturned);
          yield return list.GetRange(itemsReturned, currentChunkSize);
          itemsReturned += currentChunkSize;
     }
}

他的Linq扩展程序的用法:

Usage of his Linq Extension:

var courses = ids.ToChunks(1000)
                 .Select(chunk => Courses.Where(c => chunk.Contains(c.CourseID)))
                 .SelectMany(x => x).ToList();

我希望对我的场景采用此扩展,因此我可以使用一个简单的结构(例如ToChunks(1000)),它将ID数组拆分为1000个长段,在ID的每个部分上运行异步EF查询并合并结果一起返回到一个列表中.与手动拆分ID数组,创建for循环并在ID数组部分上单独运行查询并将结果合并回列表相比,这将是一种更干净,可重用的解决方案.

I was hoping to adopt this extension to my scenario, so I could use a simple construct such as ToChunks(1000) and it will split ID array into 1000 length long sections, run async EF query on each section of IDs and merge the result back together into a single list. It would be much cleaner and reusable solution than manually splitting ID array, creating a for loop and running queries individually over ID array sections and merging the results back into a list.

推荐答案

这是XY问题.您要实现的目标与您要问的问题不一样.即使您使此方法异步,您所需的语法也不起作用:

This is an XY problem. What you're trying to achieve isn't the same thing as what your question is asking. Your desired syntax wouldn't work, even if you made this method async:

var courses = await ids.ToChunks(1000)
                 .Select(chunk => Courses.Where(c => chunk.Contains(c.CourseID)))
                 .SelectMany(x => x).ToListAsync();

您真正想要的结果也可以轻松实现,而无需使此方法异步:

And the result you really want is just as easily accomplished without making this method async:

var courses = new List<Course>();
foreach(var chunk in ids.ToChunks(1000))
{
    courses.AddRange(await Courses.Where(c => chunk.Contains(c.CourseID)).ToListAsync());
}

注意:正如Syatoslav 指出,通过DbContext不允许您执行多个并发异步调用这一事实将阻碍通过并发获得更好性能的任何尝试.因此,无论如何您实际上都不会获得太多优势.而且在某些情况下,您可能会发现它要慢得多.

Note: As Syatoslav pointed out, any attempt to get better performance via concurrency will be thwarted by the fact that DbContext won't let you perform multiple concurrent async calls. So you really won't get much advantage to doing this asynchronously anyway. And in some cases, you might find it to be much slower.

这篇关于将非异步Linq扩展方法转换为异步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆