C# ASP.NET Core Entity Framework Core 异步 ToQueryable 比较

Posted

技术标签:

【中文标题】C# ASP.NET Core Entity Framework Core 异步 ToQueryable 比较【英文标题】:C# ASP.NET Core Entity Framework Core asynchronous ToQueryable comparison 【发布时间】:2020-04-24 16:45:48 【问题描述】:

我正在使用EfRepository<TEntity> 访问数据,我有一个DbSet<TEntity>。 我正在尝试异步访问数据,我需要它是Queryable,我目前正在使用这个:

public virtual async Task<IQueryable<TEntity>> AllAsync()

    var list = await this.DbSet.ToListAsync();
    return list.AsQueryable();

它实际上比同步使用DbSet 更快吗?

【问题讨论】:

请记住,任何时候使用 ToList 都意味着将执行枚举,将调用数据库并将结果存储在内存中,而 IQueryable 的整个想法是避免这种情况,并且只查询数据库并按需执行枚举... @DeanKuga IEnumerable&lt;T&gt;IQueryable&lt;T&gt; 都支持延迟加载,但不同之处在于编译器会为 IQueryable&lt;T&gt; 生成表达式树。我同意 OP 不应该 ToList。 【参考方案1】:

它实际上比同步使用 DbSet 更快吗?

实际上慢得多,慢得多。这将在返回内存中的 IQueryable 之前将所有实体加载到内存中。

您的附加二级存储库应直接将 DbSet 作为 IQueryable&lt;T&gt; 返回。由调用代码指定查询并同步或异步执行它。

很多人都为 EF 编写了“通用存储库”包装器。它们都增加了复杂性,而且大多数弊大于利。

【讨论】:

当然。 DbContext 是一个存储库。一个额外的通用存储库将 90% 无意义地包装 DbContext 函数,其余 10% 将是导致问题的不确定代码。【参考方案2】:

像大多数事情一样,这取决于。如果您在控制台应用程序中运行并且您的目标是尽可能快地将实体列表完全加载到内存中,那么同步调用会快一点。如果您在 Web 应用程序中运行这样的查询,那么您应该计划同时发生多个查询。线程是 Web 应用程序中的宝贵资源。同步运行查询将占用一个线程,当您的查询是 I/O 阻塞时,该线程可用于处理其他 Web 请求。因此,您可以通过进行同步调用来最大限度地延长最后一个字节的时间,但您可以通过使用异步调用来最大限度地提高系统的整体吞吐量(并避免线程不足)。

最后,你可能需要测量它。

【讨论】:

并发数据库请求是一种宝贵的资源。网络吞吐能力是一种宝贵的资源。 Web 服务器线程相对便宜。在一个进程中有数百个线程没什么大不了的。一个线程最多有 1MB 的内存用于堆栈和一些句柄。运行数百个并发查询需要数十个内核。使用异步访问数据库会显着提高应用程序吞吐量、响应时间、资源消耗或托管成本的情况非常罕见。 然而,asp.net core 3.1 经常遭受线程不足的困扰,罪魁祸首总是使用同步调用——至少 David Fowler 是这样认为的。我自己实际上并没有计算 github 中的所有问题 :-) Web 服务器框架需要限制并发请求的数量,以免使后端系统不堪重负,并在过度负载下优雅地降级。作为其中的一部分,它必须对应用程序的行为方式设定期望。 ASP.NET Core 希望您使用异步方法,因为它是一个面向异步的系统。阻塞线程会挫败它对资源管理的期望。它本身并不昂贵。

以上是关于C# ASP.NET Core Entity Framework Core 异步 ToQueryable 比较的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 和 Entity Framework Core:Linq 中的左(外)连接

在 ASP.NET Core 中使用 Entity Framework 6

根据 ASP.NET Core 和 Entity Framework Core 中的条件禁用 [必需] 属性

更新 ASP.NET Core Entity Framework 中的实体类

Asp.net core Enum as string

使用 ASP.NET Core, Entity Framework Core 和 ABP 创建N层Web应用 第二篇