实体框架在没有 ToListAsync() 的情况下使用 await

Jon*_*ood 0 c# entity-framework async-await entity-framework-core .net-core

以下代码有效。

employees.AddRange(from e in DbContext.Employees
                   select new EmployeeSummaryModel
                   {
                       Name = e.Name,
                       Email = e.Email
                   });
Run Code Online (Sandbox Code Playgroud)

但是我将如何重写此查询以使用await?请注意,我不想使用,ToListAsync()因为这会不必要地创建第二个列表?

Pan*_*vos 6

AddRange本质上是同步的。有多种选项可以使此异步,具体取决于结果的使用方式

只是一个列表

一种选择是使用返回的列表ToListAsync而不是提前创建列表:

var employees= await query.ToListAsync();
Run Code Online (Sandbox Code Playgroud)

如果第一个是空的,就没有理由有两个列表。

使用异步流

另一种选择是使用AsAsyncEnumerable异步执行查询并取回 IAsyncEnumerable。这必须迭代以将数据逐项复制到现有列表中:

await foreach(var emp in query.AsAsyncEnumerable())
{
    employees.Add(emp);
}
Run Code Online (Sandbox Code Playgroud)

如果原始列表已经包含数据,这很有用。双方AddAddRange会引起列表的内部缓冲区虽然重新分配的。

使用有容量的空列表

如果您对结果数量有一个粗略的想法,您可以创建一个具有特定容量的列表,并至少避免一些重新分配:

var employees=new List<Employee>(100);
await foreach(var emp in query.AsAsyncEnumerable())
{
    employees.Add(emp);
}
Run Code Online (Sandbox Code Playgroud)

异步管道

如果你有很多数据,你可能不应该甚至暂时将它们存储在列表中。您可以创建一个方法管道,IAsyncEnumerable在数据到达时接受、返回和处理数据,只在最后缓存它们。您可以为此使用System.Linq.Async的运算符

query.AsAsyncEnumerable()
     .Select(emp=>EnrichFromApiAsync(emp,someUrl))
     ...
     .ToListAsync();
Run Code Online (Sandbox Code Playgroud)

或将其转换为Observablewith ToObservable(),在到达管道末端时处理最终转换的数据