Этот запрос не пытается группировать данные в смысле SQL / EF Core. Там нет агрегации участвуют.
Он загружает все строки сведений, а затем группирует их в разные сегменты на клиенте. EF Core не участвует в этом, это чисто клиентская операция. Эквивалент будет выглядеть следующим образом:
var blogs = await context.Blogs
.Where(blog => blog.Url.Contains("dotnet"))
.ToListAsync();
var blogsByNum = blogs.ToLookup(t => t.BlobNumber);
Ускорение группировки
Операция пакетирования / группировки / поиска связана исключительно с процессором, поэтому единственный способ ускорить ее - чтобы распараллелить его, ie использует все процессоры для группировки данных, например:
var blogsByNum = blogs.AsParallel()
.ToLookup(t => t.BlobNumber);
ToLookup
делает более или менее, чем GroupBy().ToList()
- группирует строки в сегменты на основе ключа
Группировка во время загрузки
Другой подход заключается в том, чтобы загрузить результаты асинхронно и поместить их в корзины по мере их поступления. Для этого нам нужно AsAsyncEnumerable()
. ToListAsync()
возвращает все результаты сразу, поэтому его нельзя использовать.
Этот подход очень похож на то, что делает ToLookup
.
var blogs = await context.Blogs
.Where(blog => blog.Url.Contains("dotnet"));
var blogsByNum=new Dictionary<string,List<Blog>>();
await foreach(var blog in blogs.AsAsyncEnumerable())
{
if(blogsByNum.TryGetValue(blog.BlobNumber,out var blogList))
{
blogList.Add(blog);
}
else
{
blogsByNum[blog.BlobNumber=new List<Blog>(100){blog};
}
}
Запрос выполняется при вызове AsAsyncEnumerable()
. Результаты поступают асинхронно, поэтому теперь мы можем добавлять их в сегменты во время итерации.
Параметр capacity
используется в конструкторе списка, чтобы избежать перераспределения внутреннего буфера списка.
Использование System.LINQ.Asyn c
Все было бы намного проще, если бы у нас были операции LINQ для самого IAsyncEnumerable <>. Это расширение namespace обеспечивает именно это. Он разработан командой ReactiveX. Он доступен через NuGet , а текущая основная версия - 4.0.
С этим мы можем просто написать:
var blogs = await context.Blogs
.Where(blog => blog.Url.Contains("dotnet"));
var blogsByNum=await blogs.AsAsyncEnumerable() individual rows asynchronously
.ToLookupAsync(blog=>blog.BlobNumber);
или
var blogsByNum=await blogs.AsAsyncEnumerable()
.GroupBy(blog=>blog.BlobNumber)
.Select(b=>b)
.ToListAsync();