【问题标题】:Entity Framework using await without ToListAsync()使用没有 ToListAsync() 的等待的实体框架
【发布时间】:2021-01-08 17:28:33
【问题描述】:

以下代码有效。

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

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

【问题讨论】:

  • AddRange 本质上是同步的。 IEnumerable<> 必须已经存在。 employees 是什么?它是否包含任何其他数据?为什么不使用ToListAsync()
  • @PanagiotisKanavos:您只需要相信我,我需要将其添加到现有列表中。解释我所做的一切超出了本网站的范围。
  • @PanagiotisKanavos:我正在尝试将结果添加到现有列表而不创建第二个列表。
  • 我不确定这里(或可能)有两个列表。在执行之前,这应该是一个表达式树。
  • @JonathanWood “我正在努力消除这种浪费” 你能否合理地支持没有这份清单的好处(我什至不确定是否仍然存在编译器优化后)是否超过了您要实现的目标的复杂性? EF 从来不是以性能为前提,而是以易用性为前提。如果您希望性能达到这样的程度,则不应一开始就使用 EF,从而使问题变得毫无意义。最重要的是,您似乎忽略了异步任务管理的开销,这比在内存中拥有单个列表要大得多。

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


【解决方案1】:

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();

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

【讨论】:

    【解决方案2】:

    我不知道在这种情况下如何使用 AddRange,但使用最新的 EF Core 和 C# 8.0 async streams 您可以使用 AsAsyncEnumerable 方法,结果是:

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

    【讨论】:

    • 没有newreturn 部分的ToListAsync implementation
    • @IvanStoev:不,不是。 ToListAsync() 分配和填充List&lt;&gt;。此代码填充我现有的列表。
    • @JonathanWood 这正是我写的。 没有new(列表)和return(列表)部分
    • @IvanStoev:好吧,好吧。 new 是我想要消除的。
    • 为了获得更好的性能,您可以考虑通过使用ConfigureAwait 扩展方法避免在每个循环上恢复同步上下文:await foreach(var e in query.AsAsyncEnumerable().ConfigureAwait(false))
    猜你喜欢
    • 1970-01-01
    • 2011-08-15
    • 1970-01-01
    • 2020-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多