以下代码有效。
employees.AddRange(from e in DbContext.Employees
select new EmployeeSummaryModel
{
Name = e.Name,
Email = e.Email
});
但是我将如何重写此查询以使用await
?请注意,我不想使用ToListAsync()
,因为这会不必要地创建第二个列表?
答案 0 :(得分:6)
AddRange
本质上是同步的。有多种方法可以使此操作异步,具体取决于结果的使用方式
仅一个列表
一种选择是使用ToListAsync
返回的列表,而不是预先创建列表:
var employees= await query.ToListAsync();
如果第一个列表为空,则没有理由列出两个列表。
使用异步流
另一个选择是使用AsAsyncEnumerable异步执行查询并获取IAsyncEnumerable。必须迭代此操作才能将数据逐项复制到现有列表项:
await foreach(var emp in query.AsAsyncEnumerable())
{
employees.Add(emp);
}
如果原始列表已经包含数据,这将很有用。 Add
和AddRange
都将导致列表内部缓冲区的重新分配。
使用具有容量的空列表
如果您对结果的数量有一个粗略的了解,则可以创建一个具有特定容量的列表,并至少避免一些重新分配:
var employees=new List<Employee>(100);
await foreach(var emp in query.AsAsyncEnumerable())
{
employees.Add(emp);
}
异步管道
但是,如果您有大量数据,则可能甚至不应暂时将它们存储在列表中。您可以创建一个方法管道来接受和返回IAsyncEnumerable
并在数据到达时对其进行处理,仅在最后将其缓存。您可以为此使用System.Linq.Async的运算符
query.AsAsyncEnumerable()
.Select(emp=>EnrichFromApiAsync(emp,someUrl))
...
.ToListAsync();
或使用Observable
将其转换为ToObservable()
,在最终转换的数据到达管道末端时对其进行处理
答案 1 :(得分:3)
在这种情况下,我看不到如何使用AddRange
,但是使用最新的EF Core和C#8.0 async streams,您可以使用AsAsyncEnumerable
方法,结果是:
var query = ...;
await foreach(var e in query.AsAsyncEnumerable())
{
employees.Add(e);
}