实体框架使用没有ToListAsync()的await

时间:2020-09-22 14:48:16

标签: c# entity-framework .net-core async-await entity-framework-core

以下代码有效。

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

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

2 个答案:

答案 0 :(得分:6)

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

仅一个列表

一种选择是使用ToListAsync返回的列表,而不是预先创建列表:

var employees= await query.ToListAsync();

如果第一个列表为空,则没有理由列出两个列表。

使用异步流

另一个选择是使用AsAsyncEnumerable异步执行查询并获取IAsyncEnumerable。必须迭代此操作才能将数据逐项复制到现有列表项:

await foreach(var emp in query.AsAsyncEnumerable())
{
    employees.Add(emp);
}

如果原始列表已经包含数据,这将很有用。 AddAddRange都将导致列表内部缓冲区的重新分配。

使用具有容量的空列表

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

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);
    }
相关问题