entity-framework - ASP.NET Core 3 EF Framework - 运行大量查询时出现瞬时故障
问题描述
我正在将 EF Core 用于 ASP.NET 网站。在一种方法中,我有大约 7 个类似于(简单示例)的复杂查询:
var query1 = context.Playarea
.Include(x => x.Cats)
.Where(x => x.Cats.Any())
.SelectMany(x => x.Cats.Select(y => new MyClass(x.Id, y.Name, y.Age))).ToList();
var query2 = context.Playarea
.Include( x => x.Dogs)
.Where(x => x.Dogs.Any())
.SelectMany(x => x.Dogs, (x, y) => new MyClass(x.Id, y.Name, y.Age)).ToList();
var query3 = context.Playarea
.Include( x => x.Dogs)
.Include( x => x.Leads)
.Where(x => x.Dogs.Any())
.SelectMany(x => x.Dogs, (x, y) => new MyClass(x.Id, y.Leads.Name, y.Age)).ToList();
var query4 = context.Playarea
.Include( x => x.Birds)
.Where(x => x.Birds.Any())
.SelectMany(x => x.Birds, (x, y) => new MyClass(x.Id, y.Name, y.Age)).ToList();
return query1.Concat(query2).Concat(query3).Concat(query4).ToList();
这通常有效,但有时页面会崩溃:
An unhandled exception was thrown by the application. System.InvalidOperationException: An exception has been raised that is likely due to a transient failure. Consider enabling transient error resiliency by adding 'EnableRetryOnFailure()' to the 'UseSqlServer' call.
---> Microsoft.Data.SqlClient.SqlException (0x80131904): A transport-level error has occurred when receiving results from the server. (provider: TCP Provider, error: 0 - The semaphore timeout period has expired.)
---> System.ComponentModel.Win32Exception (121): The semaphore timeout period has expired.
我知道我可以添加“EnableRetryOnFailure()”,但我担心真正的原因是它同时运行多个查询。有没有办法让这个更安全、更高效?
我已经尝试查找指南,但是如果您一次尝试大量查询,它们都没有涵盖该怎么做。
解决方案
老实说,我认为这可能与您的代码关系不大,而与某种网络或数据库问题有关。虽然有一些方法可以改进此代码,但它确实没有理由不应该始终如一地工作。
也就是说,您正在阻止所有这些查询,这绝不是一个好主意。EF Core 所做的一切都是异步的。同步方法仅阻塞异步方法,并且仅存在于无法使用异步的场景中,例如桌面应用程序中的事件委托等。简而言之,您应该始终使用异步方法,除非您遇到无法使用的特定情况。在 ASP.NET Core 应用程序中,不存在这种情况,因此应始终使用 async。长短,使用ToListAsync
代替ToList
和await
每一行。
接下来,您的 where 子句没有意义。例如,无论您只在有狗的项目上选择很多,还是在所有项目上选择很多,无论它们是否有狗,您仍然会得到相同的结果。无论哪种方式,所有这些都将在数据库中运行,因此一种方法或另一种方法甚至没有性能优势。Include
如果您从关系中选择,您也不需要使用。EF 足够聪明,可以根据它需要返回的数据发出连接。
var query1 = await context.Playarea
.SelectMany(x => x.Cats.Select(y => new MyClass(x.Id, y.Name, y.Age))).ToListAsync();
还有你的两个狗查询的问题。两个查询将返回相同的结果。唯一的区别是一组将具有y.Name
,而另一组将具有y.Leads.Name
作为Name
值。包含的行为Leads
不会以某种方式过滤掉没有的结果,Leads
当然,第一个查询根本没有过滤,无论哪种方式。我想你会想要类似下面的东西:
var query3 = await context.Playarea
.SelectMany(x => x.Dogs, (x, y) => new MyClass(x.Id, y.Leads is null ? y.Name : y.Leads.Name, y.Age)).ToListAsync();
换句话说,Leads.Name
如果关系存在,将使用,Name
否则将回退。
推荐阅读
- google-cloud-platform - GCP OAuth 同意屏幕 - 显示自定义数据
- c++ - 代码在 C++ 中运行得非常好,但在 Leetcode 上的 python 中提供 TLE
- c# - 无法处理 Selenium C# 中边缘铬浏览器中弹出的下载
- symfony - Symfony 使用 JMS 序列化器从实体存储库返回 json
- c# - 我们可以在 ASP.NET MVC 中使用 [HttpPost] 从数据库中获取数据并使用 [HttpGet] 将数据插入数据库吗?
- python - 基于优先级映射数据框列
- typescript - 泛型函数类型中的 Promise 结果值未知
- node.js - fs.writeFile 创建空文件(node.js)
- visual-studio-code - 删除 Visual Studio Code 中的“设置为解释器”标签
- ibm-cloud - 如何使用 Rest API 从 Block Storage 订购快照空间